diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright Daniel YU (c) 2018
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Daniel YU nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,1 @@
+# yam-app
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import           Distribution.Simple
+main = defaultMain
diff --git a/src/Yam/App.hs b/src/Yam/App.hs
new file mode 100644
--- /dev/null
+++ b/src/Yam/App.hs
@@ -0,0 +1,147 @@
+{-# LANGUAGE FlexibleContexts      #-}
+{-# LANGUAGE FlexibleInstances     #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE OverloadedStrings     #-}
+{-# LANGUAGE TypeSynonymInstances  #-}
+
+module Yam.App(
+    module Yam.App.Context
+  , module Yam.Import
+  , module Yam.Event
+  , module Yam.Logger
+  , module Yam.Prop
+  , module Yam.Transaction
+  , runAppM
+  , AppM
+  , RunMode(..)
+  , defaultContext
+  , registerEventHandler
+  , registerEventHandler'
+  , evalPropOrDefault
+  , evalProp
+  ) where
+
+import           Yam.App.Context
+import           Yam.Event
+import           Yam.Import
+import           Yam.Logger
+import           Yam.Prop
+import           Yam.Transaction
+
+import           Control.Monad.Trans.Control (MonadBaseControl)
+import           Data.Aeson
+import           Data.Foldable
+import           Data.Proxy
+
+
+type AppM = ReaderT YamContext
+
+data RunMode = Development | Production deriving (Show, Eq)
+
+instance FromJSON RunMode where
+  parseJSON v = go <$> parseJSON v
+    where go :: Text -> RunMode
+          go "production" = Production
+          go _            = Development
+
+runAppM :: (Monad m) => YamContext -> AppM m a -> m a
+runAppM = flip runReaderT
+
+instance MonadIO m => HasYamContext (AppM m) where
+  yamContext = ask
+
+defaultContext :: IO YamContext
+defaultContext = do
+  context  <- emptyContext
+  logger   <- stdoutLogger
+  runAppM context $ do
+    setExtension keyLogger logger
+    loadProps
+    initLogger
+  return context
+
+loadProps :: AppM IO ()
+loadProps = do
+  context <- ask
+  let showLog :: IO PropertySource -> IO PropertySource
+      showLog a = do
+        src@(f,_) <- a
+        runAppM context $ debugLn $ "Load Config " <> f <> " .."
+        return src
+  source <- liftIO $ do
+    cmdSource <- showLog loadCommandLineArgs
+    envSource <- showLog loadEnv
+    let baseSource@(_,v) = mergePropertySource [cmdSource, envSource]
+    mayConf   <- runProp v $ getProp "config"
+    case mayConf of
+      Nothing -> return baseSource
+      Just c  -> do
+        confSource@(_,cv) <- showLog $ loadYaml c
+        configs           <- runProp cv $ getPropOrDefault [] "configs"
+        addtionalSource   <- mapM (showLog . loadYaml) configs
+        return $ mergePropertySource $ baseSource:confSource:addtionalSource
+  setExtension keyProp source
+
+initLogger :: AppM IO ()
+initLogger = do
+  mayLogFile <- getProp "log.file"
+  logRank    <- getPropOrDefault DEBUG "log.level"
+  withLoggerRank logRank $ case mayLogFile of
+    Just file -> do
+      newLogger <- liftIO $ fileLogger file
+      setExtension keyLogger logger
+    Nothing   -> return ()
+
+enable :: FromJSON a => Text -> Bool -> Text -> (Maybe a -> AppM IO ()) -> AppM IO ()
+enable keyEnable def key action = do
+        enables <- getPropOrDefault def keyEnable
+        when enables $ getProp key >>= action
+
+keyLogger :: Text
+keyLogger = "Extension.Logger"
+
+instance MonadIO m => MonadLogger (AppM m) where
+  loggerConfig     = requireExtension keyLogger
+  withLoggerConfig l action = setExtension keyLogger l >> action
+
+keyProp :: Text
+keyProp = "Extension.Prop"
+
+instance MonadIO m => MonadProp (AppM m) where
+  propertySource = requireExtension keyProp
+
+evalProp :: FromJSON a => YamContext -> Text -> IO (Maybe a)
+evalProp c = runAppM c . getProp
+
+evalPropOrDefault :: FromJSON a => a -> YamContext -> Text -> IO a
+evalPropOrDefault a c key = fromMaybe a <$> evalProp c key
+
+keyTransaction :: Text
+keyTransaction = "Extension.Transaction"
+keySecondaryTransaction :: Text
+keySecondaryTransaction = "Extension.Transaction.Secondary"
+
+instance (MonadIO m, MonadBaseControl IO m) => MonadTransaction (AppM m) where
+  connectionPool    = requireExtension keyTransaction
+  setConnectionPool p s = do
+    setExtension keyTransaction p
+    forM_ s (setExtension keySecondaryTransaction)
+
+keyEvent :: Text
+keyEvent = "Extension.Event."
+
+instance MonadIO m => MonadEvent (AppM m) where
+  eventHandler proxy = getExtensionOrDefault [] $ keyEvent <> cs (eventKey proxy)
+
+registerEventHandler :: (MonadIO m, Event e) => Proxy e -> (e -> AppM IO ()) -> AppM m ()
+registerEventHandler p = registerEventHandler' p Nothing
+
+registerEventHandler' :: (MonadIO m, Event e) => Proxy e -> Maybe Text -> (e -> AppM IO ()) -> AppM m ()
+registerEventHandler' p hname h = do
+  hs      <- eventHandler p
+  context <- ask
+  let key  = keyEvent <> cs (eventKey p)
+      h'   = runAppM context . h
+      name = fromMaybe (key <> "." <> showText (length hs + 1)) hname
+  infoLn $ "Register eventHandler " <> name <> " for " <> key
+  setExtension key (h':hs)
diff --git a/src/Yam/App/Context.hs b/src/Yam/App/Context.hs
new file mode 100644
--- /dev/null
+++ b/src/Yam/App/Context.hs
@@ -0,0 +1,56 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Yam.App.Context(
+    YamContext(..)
+  , HasYamContext(..)
+  , requireExtension
+  , getExtensionOrDefault
+  , setExtension
+  , lockExtenstion
+  , emptyContext
+  ) where
+
+import           Yam.Import
+import           Yam.Logger
+
+import qualified Control.Concurrent.Map as M
+import           Data.Dynamic
+
+type YamExtension = M.Map Text Dynamic
+
+newtype YamContext = YamContext {extensions :: YamExtension}
+
+emptyContext :: IO YamContext
+emptyContext = YamContext <$> M.empty
+
+class MonadIO m => HasYamContext m where
+  yamContext :: m YamContext
+
+extensionLockKey :: Text
+extensionLockKey = "Extension.Lock"
+
+extension :: HasYamContext m => m YamExtension
+extension = extensions <$> yamContext
+
+requireExtension :: (HasYamContext m, Typeable a) => Text -> m a
+requireExtension key = extension >>= liftIO . M.lookup key >>= get . (fromDynamic =<<)
+  where get Nothing  = error $ "Module " <> cs key <> " not loaded"
+        get (Just r) = return r
+
+getExtensionOrDefault :: (HasYamContext m, Typeable a) => a -> Text -> m a
+getExtensionOrDefault a key = (fromMaybe a . (fromDynamic =<<)) <$> (extension >>= liftIO . M.lookup key)
+
+setExtension :: (MonadLogger m, HasYamContext m, Typeable a) => Text -> a -> m ()
+setExtension key a = do
+  checkLock
+  void $ extension >>= liftIO . M.insert key (toDyn a)
+  when (extensionLockKey /= key)
+    (debugLn $ "Register extension <<" <> key <> ">>")
+
+checkLock :: HasYamContext m => m ()
+checkLock = getExtensionOrDefault False extensionLockKey >>= go
+  where go True = error "Extension has freezed, cannot modify now"
+        go _    = return ()
+
+lockExtenstion :: (MonadLogger m, HasYamContext m)  => m ()
+lockExtenstion = setExtension extensionLockKey True
diff --git a/src/Yam/Event.hs b/src/Yam/Event.hs
new file mode 100644
--- /dev/null
+++ b/src/Yam/Event.hs
@@ -0,0 +1,38 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Yam.Event(
+    MonadEvent(..)
+  , Event(..)
+  , thenNotify
+  ) where
+import           Yam.Import
+
+import           Yam.Logger
+
+import           Control.Exception (SomeException)
+import           Data.Aeson
+import           Data.Proxy
+import           Data.Typeable
+
+encodeToText :: ToJSON e => e -> Text
+encodeToText = cs . encode
+
+class (Monad m) => MonadEvent m where
+  eventHandler :: Event e => Proxy e -> m [e -> IO ()]
+
+class (ToJSON e, Typeable e) => Event e where
+  eventKey :: Proxy e -> String
+  eventKey = show . typeRep
+
+thenNotify :: (Event e, MonadLogger m, MonadMask m, MonadEvent m) => m a -> e -> m a
+thenNotify ma e = do
+  a <- ma
+  let printStack :: (Event e, MonadLogger m) => e -> SomeException -> m ()
+      printStack e x = do
+          errorLn $ "Event "      <> encodeToText e <> " Failed!"
+          errorLn $ "Exception: " <> showText x
+  withLoggerName "Event" $ do
+    infoLn  $ "Event " <> encodeToText e <> " Received!"
+    ec  <- eventHandler (Proxy :: Proxy e)
+    mapM_ (\h -> liftIO (h e) `catchAll` printStack e) ec
+  return a
diff --git a/src/Yam/Import.hs b/src/Yam/Import.hs
new file mode 100644
--- /dev/null
+++ b/src/Yam/Import.hs
@@ -0,0 +1,61 @@
+module Yam.Import(
+    Text
+  , pack
+  , cs
+  , showText
+  , lift
+  , MonadIO
+  , liftIO
+  , when
+  , unless
+  , void
+  , (<>)
+  , myThreadId
+  , ThreadId
+  , killThread
+  , fromMaybe
+  , maybe
+  , mapMaybe
+  , catMaybes
+  , selectMaybe
+  , finally
+  , MonadMask
+  , MonadThrow
+  , MonadCatch
+  , catchAll
+  , runReaderT
+  , ReaderT
+  , ask
+  , Generic
+  , UTCTime
+  , randomHex
+  , Proxy(..)
+  ) where
+
+import           Control.Concurrent
+import           Control.Monad              (unless, void, when)
+import           Control.Monad.Catch
+import           Control.Monad.IO.Class     (MonadIO, liftIO)
+import           Control.Monad.Trans.Class
+import           Control.Monad.Trans.Reader (ReaderT, ask, runReaderT)
+import           Data.Maybe
+import           Data.Monoid                ((<>))
+import           Data.Proxy
+import           Data.String.Conversions    (cs)
+import           Data.Text                  (Text, pack)
+import           Data.Time.Clock
+import           GHC.Generics
+import           System.Random              (newStdGen, randoms)
+
+
+showText :: Show a => a -> Text
+showText = cs . show
+
+_hex = ['0'..'9'] <> ['a'..'f']
+
+randomHex :: Int -> IO Text
+randomHex n = (pack . map (go _hex 16) . take n . randoms) <$> newStdGen
+          where go gs l v = gs !! mod v l
+
+selectMaybe :: [Maybe a] -> Maybe a
+selectMaybe = listToMaybe . catMaybes
diff --git a/src/Yam/Logger.hs b/src/Yam/Logger.hs
new file mode 100644
--- /dev/null
+++ b/src/Yam/Logger.hs
@@ -0,0 +1,144 @@
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RankNTypes        #-}
+
+module Yam.Logger(
+    LogRank(..)
+  , LoggerConfig(..)
+  , MonadLogger(..)
+  , logL
+  , logLn
+  , errorLn
+  , warnLn
+  , infoLn
+  , debugLn
+  , traceLn
+  , stdoutLogger
+  , fileLogger
+  , withLoggerRank
+  , withLoggerName
+  ) where
+
+import           Yam.Import
+
+import qualified Control.Concurrent.Map     as M
+import           Control.Monad.Catch        (bracket_)
+import           Control.Monad.Trans.Reader
+import           Data.Aeson
+import           System.Log.FastLogger
+
+data LogRank = TRACE
+             | DEBUG
+             | INFO
+             | WARN
+             | ERROR
+             deriving (Show,Eq,Ord)
+
+instance FromJSON LogRank where
+  parseJSON v = go <$> parseJSON v
+    where go :: Text -> LogRank
+          go "trace" = TRACE
+          go "debug" = DEBUG
+          go "info"  = INFO
+          go "warn"  = WARN
+          go "error" = ERROR
+          go  _      = INFO
+
+type LoggerCache = M.Map ThreadId Text
+type LoggerFunc  = Text -> Maybe Text -> LogRank -> LogStr -> IO ()
+data LoggerConfig = LoggerConfig
+   { logger :: LoggerFunc
+   , clock  :: IO FormattedTime
+   , rank   :: LogRank
+   , name   :: LoggerCache
+   }
+
+class (MonadIO m) => MonadLogger m where
+  loggerConfig     :: m LoggerConfig
+  withLoggerConfig :: LoggerConfig -> m a -> m a
+
+instance (MonadIO m) => MonadLogger (ReaderT LoggerConfig m) where
+  loggerConfig     = ask
+  withLoggerConfig = withReaderT . const
+
+logL :: (MonadLogger m) => forall msg . (ToLogStr msg) => LogRank -> msg -> m ()
+logL r msg = do
+  conf    <- loggerConfig
+  mayName <- fetchName
+  liftIO $ when (r >= rank conf) $ do
+    now      <- clock conf
+    logger conf (cs now) (cs <$> mayName) r (toLogStr msg)
+
+fetchName :: MonadLogger m => m (Maybe Text)
+fetchName = do
+  conf <- loggerConfig
+  liftIO $ do
+    threadId <- myThreadId
+    M.lookup threadId $ name conf
+
+setName :: MonadLogger m => Maybe Text -> m ()
+setName m = do
+  conf <- loggerConfig
+  liftIO $ myThreadId >>= void . go m (name conf)
+  where go (Just m) cache tid = M.insert tid m cache
+        go _        cache tid = M.delete tid   cache
+
+logLn :: (MonadLogger m) => LogRank -> Text -> m ()
+logLn l msg = logL l $ msg <> "\n"
+
+traceLn :: (MonadLogger m) => Text -> m ()
+traceLn = logLn TRACE
+debugLn :: (MonadLogger m) => Text -> m ()
+debugLn = logLn DEBUG
+infoLn  :: (MonadLogger m) => Text -> m ()
+infoLn  = logLn INFO
+warnLn  :: (MonadLogger m) => Text -> m ()
+warnLn  = logLn WARN
+errorLn :: (MonadLogger m) => Text -> m ()
+errorLn = logLn ERROR
+
+defaultLoggerConfig :: LoggerFunc -> IO LoggerConfig
+defaultLoggerConfig func = do
+     nm        <- M.empty
+     timeCache <- newTimeCache "%F %X"
+     return $ LoggerConfig func timeCache DEBUG nm
+
+stdoutLogger :: IO LoggerConfig
+stdoutLogger = newStdoutLoggerSet 4096 >>= newLog
+
+fileLogger :: FilePath -> IO LoggerConfig
+fileLogger file = newFileLoggerSet 4096 file >>= newLog
+
+newLog :: LoggerSet -> IO LoggerConfig
+newLog = defaultLoggerConfig . mkLogger . pushLogStr
+  where mkLogger :: FastLogger -> LoggerFunc
+        mkLogger logger time mayName rank msg = do
+          thread  <- myThreadId
+          let name = time
+                  <> " ["
+                  <> showText thread
+                  <> "] "
+                  <> showText rank
+                  <> " "
+                  <> fromMaybe "" mayName
+                  <> " - "
+          logger $ toLogStr name <> msg
+
+withLoggerRank :: (MonadLogger m) => LogRank -> m a -> m a
+withLoggerRank = withLogger . go
+  where go rk conf = conf {rank = rk}
+
+withLoggerName :: (MonadLogger m, MonadMask m) => Text -> m a -> m a
+withLoggerName nm action = do
+  mayName <- fetchName
+  let mayName' = Just $ merge nm mayName
+  bracket_ (setName mayName') (setName mayName) action
+  where merge n (Just v) = v <> "." <> n
+        merge n _        = n
+
+withLogger :: (MonadLogger m) => (LoggerConfig -> LoggerConfig) -> m a -> m a
+withLogger modify action = do
+  conf    <- loggerConfig
+  withLoggerConfig (modify conf) action
+
+
diff --git a/src/Yam/Logger/MonadLogger.hs b/src/Yam/Logger/MonadLogger.hs
new file mode 100644
--- /dev/null
+++ b/src/Yam/Logger/MonadLogger.hs
@@ -0,0 +1,23 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Yam.Logger.MonadLogger(
+     toMonadLogger
+   , runLoggingT
+   ) where
+
+import           Yam.Import
+import           Yam.Logger
+
+import           Control.Monad.Logger
+
+type LogFunc = Loc -> LogSource -> LogLevel -> LogStr -> IO ()
+
+toMonadLogger :: (Yam.Logger.MonadLogger m) => m LogFunc
+toMonadLogger = mkLogger <$> loggerConfig
+   where toRank LevelDebug = DEBUG
+         toRank LevelInfo  = INFO
+         toRank LevelWarn  = WARN
+         toRank LevelError = ERROR
+         toRank _          = INFO
+         mkLogger :: LoggerConfig -> LogFunc
+         mkLogger context  _ name level msg = runReaderT (withLoggerName name $ logL (toRank level) (msg <> "\n")) context
diff --git a/src/Yam/Logger/WaiLogger.hs b/src/Yam/Logger/WaiLogger.hs
new file mode 100644
--- /dev/null
+++ b/src/Yam/Logger/WaiLogger.hs
@@ -0,0 +1,13 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Yam.Logger.WaiLogger where
+
+import           Yam.Import
+import           Yam.Logger
+
+import           Network.Wai.Logger
+
+toWaiLogger :: (MonadLogger m) => m ApacheLogger
+toWaiLogger = do mkLogger <- flip runReaderT <$> loggerConfig
+                 liftIO   $  apacheLogger
+                         <$> initLogger FromFallback (LogCallback (mkLogger . logL INFO) $ return ()) (return "")
diff --git a/src/Yam/Prop.hs b/src/Yam/Prop.hs
new file mode 100644
--- /dev/null
+++ b/src/Yam/Prop.hs
@@ -0,0 +1,116 @@
+{-# LANGUAGE FlexibleInstances    #-}
+{-# LANGUAGE OverloadedStrings    #-}
+{-# LANGUAGE TupleSections        #-}
+{-# LANGUAGE TypeSynonymInstances #-}
+
+module Yam.Prop(
+    PropertySource
+  , emptyPropertySource
+  , MonadProp(..)
+  , getProp
+  , getPropOrDefault
+  , requiredProp
+  , tryLoadYaml
+  , loadYaml
+  , loadEnv
+  , loadCommandLineArgs
+  , mergePropertySource
+  , runProp
+  ) where
+
+import           Yam.Import
+
+import           Data.Aeson          (Result (..), fromJSON)
+import qualified Data.HashMap.Strict as M
+import           Data.List           (foldl')
+import qualified Data.Text           as T
+import           Data.Yaml
+import           System.Directory    (doesFileExist, getFileSize)
+import           System.Environment  (getArgs, getEnvironment)
+
+type PropertySource = (Text, Value)
+
+emptyPropertySource :: PropertySource
+emptyPropertySource = ("DEFAULT", Null)
+
+class Monad m => MonadProp m where
+  propertySource :: m PropertySource
+
+type ValueProperty = ReaderT PropertySource
+
+instance (Monad m) => MonadProp (ValueProperty m) where
+  propertySource = ask
+
+runProp :: (Monad m) => Value -> ValueProperty m a -> m a
+runProp v ma = runReaderT ma ("NO_NAME", v)
+
+getPropOrDefault :: (FromJSON a, MonadProp m) => a -> Text -> m a
+getPropOrDefault def key = fromMaybe def <$> getProp key
+
+requiredProp :: (FromJSON a, MonadProp m) => Text -> m a
+requiredProp key = getProp key >>= maybe (error $ "key " <> cs key <> " not found") return
+
+getProp :: (FromJSON a, MonadProp m) => Text -> m (Maybe a)
+getProp key  = propertySource >>= go (splitKey key)
+  where go :: (FromJSON a, Monad m) => [Text] -> PropertySource -> m (Maybe a)
+        go hs (s,v) = to s $ foldl' fetch v hs
+        fetch :: Value -> Text -> Value
+        fetch (Object map) h = fromMaybe Null $ M.lookup h map
+        fetch _            _ = Null
+        to :: (FromJSON a, Monad m) => Text -> Value -> m (Maybe a)
+        to _ Null = return Nothing
+        to s v    = case fromJSON v of
+          Error   e -> error e
+          Success a -> return (Just a)
+splitKey :: Text -> [Text]
+splitKey k | T.null k  = []
+           | otherwise = T.split (=='.') k
+
+tryLoadYaml :: (MonadIO m) => FilePath -> m (Maybe PropertySource)
+tryLoadYaml file = do
+  exists <- liftIO $ doesFileExist file
+  if exists
+    then do
+      size <- liftIO $ getFileSize file
+      if size > 0 then
+        Just . (cs file,) <$> (liftIO (decodeFile file) >>= maybe (error $ file <> " load failed") return)
+      else return Nothing
+    else return Nothing
+
+loadYaml :: (MonadIO m) => FilePath -> m PropertySource
+loadYaml file = tryLoadYaml file >>= maybe (error $ file <> " not found") return
+
+loadEnv :: (MonadIO m) => m PropertySource
+loadEnv = do
+  env <- liftIO getEnvironment
+  return ("System.Environment", convertValue env)
+
+loadCommandLineArgs :: (MonadIO m) => m PropertySource
+loadCommandLineArgs = do
+  args <- liftIO getArgs
+  return ("CommandLine", convertValue $ go args)
+  where go      = mapMaybe (select . break (=='='))
+        select ("", _)     = Nothing
+        select (_, "")     = Nothing
+        select (k, '=':vs) = Just (k,vs)
+        select kvs         = Just kvs
+
+convertValue :: [(String, String)] -> Value
+convertValue = toValue . mapMaybe go
+  where go :: (String, String) -> Maybe ([Text], Value)
+        go (k,v) = (toKey $ cs k, ) <$> decode (cs v)
+        toKey    = splitKey . T.toLower . T.map (\a -> if a == '_' then '.' else a)
+        to :: ([Text], Value) -> Maybe (Text, [([Text], Value)])
+        to (h:hs, v) = Just (h, [(hs,v)])
+        to ([],   _) = Nothing
+        toValue :: [([Text], Value)] -> Value
+        toValue [([],value)] = value
+        toValue vs           = Object $ M.map toValue $ M.fromListWith (++) $ mapMaybe to vs
+
+mergePropertySource :: [PropertySource] -> PropertySource
+mergePropertySource = foldl' merge emptyPropertySource
+  where merge :: (Text, Value) -> (Text, Value) -> (Text, Value)
+        merge (n1,v1) (n2,v2)        = (n1, merge' v1 v2)
+        merge' Null    a             = a
+        merge' (Object a) (Object b) = Object (M.unionWith merge' a b)
+        merge' a       _             = a
diff --git a/src/Yam/Transaction.hs b/src/Yam/Transaction.hs
new file mode 100644
--- /dev/null
+++ b/src/Yam/Transaction.hs
@@ -0,0 +1,98 @@
+{-# LANGUAGE DataKinds             #-}
+{-# LANGUAGE DeriveAnyClass        #-}
+{-# LANGUAGE DeriveGeneric         #-}
+{-# LANGUAGE FlexibleContexts      #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE OverloadedStrings     #-}
+{-# LANGUAGE RankNTypes            #-}
+{-# LANGUAGE TypeOperators         #-}
+
+module Yam.Transaction(
+    Transaction
+  , TransactionPool(..)
+  , DataSource(..)
+  , MonadTransaction(..)
+  , DataSourceConnector
+  , DataSourceProvider
+  , HasDataSource(..)
+  , runTrans
+  , selectValue
+  , selectNow
+  , now
+  , initDataSource
+  ) where
+
+import           Yam.Import
+import           Yam.Logger
+import           Yam.Logger.MonadLogger
+
+import           Control.Monad.Trans.Control (MonadBaseControl)
+import           Data.Aeson
+import           Data.Default
+import qualified Data.Map                    as M
+import           Data.Pool
+import           Data.Proxy
+import           Data.Reflection
+import           Database.Persist.Sql
+import           GHC.TypeLits
+
+-- SqlPersistT ~ ReaderT SqlBackend
+type Transaction  = SqlPersistT IO
+
+type TransactionPool = Pool SqlBackend
+
+data DataSource = DataSource
+  { dbtype  :: Text
+  , conn    :: Text
+  , thread  :: Int
+  , migrate :: Bool
+  , extra   :: Maybe (M.Map Text Text)
+  } deriving (Show, Generic, FromJSON)
+
+instance Default DataSource where
+  def = DataSource "sqlite" ":memory:" 10 True Nothing
+
+class (MonadIO m, MonadBaseControl IO m, MonadLogger m) => MonadTransaction m where
+  connectionPool :: m TransactionPool
+  setConnectionPool :: TransactionPool -> Maybe TransactionPool -> m ()
+  secondaryPool  :: m (Maybe TransactionPool)
+  secondaryPool = return Nothing
+
+type DataSourceConnector m a = LogFunc -> DataSource -> (TransactionPool -> m a) -> m a
+type DataSourceProvider  m a = (Text, DataSourceConnector m a)
+
+class HasDataSource ds where
+  connector :: MonadTransaction m => Proxy ds -> DataSourceConnector m a
+
+initDataSource :: MonadTransaction m => [DataSourceProvider m a] -> DataSource -> Maybe DataSource -> m a -> m a
+initDataSource maps ds ds2nd action = let map = M.fromList maps in go map (Just ds) $ go map ds2nd action
+  where go _    Nothing  action = action
+        go map (Just ds) action = case M.lookup (dbtype ds) map of
+            Nothing -> error $ "Datasource " <> cs (dbtype ds) <> " not supported"
+            Just db -> do
+              logger <- toMonadLogger
+              db logger ds $ \p -> setConnectionPool p Nothing >> action
+
+runTrans :: MonadTransaction m => Transaction a -> m a
+runTrans trans = connectionPool >>= liftIO . runSqlPool trans
+
+runSecondaryTrans :: MonadTransaction m => Transaction a -> m a
+runSecondaryTrans trans = do
+  pool <- secondaryPool
+  case pool of
+    Nothing -> error "Secondary Pool not exists"
+    Just p  -> liftIO $ runSqlPool trans p
+
+selectNow :: Transaction UTCTime
+selectNow = head <$> (ask >>= dbNow . connRDBMS)
+  where dbNow :: Text -> Transaction [UTCTime]
+        dbNow "sqlite"     = selectValue "SELECT CURRENT_TIMESTAMP"
+        dbNow "postgresql" = selectValue "SELECT CURRENT_TIMESTAMP"
+        dbNow "oracle"     = selectValue "SELECT SYSDATE FROM DUAL"
+        dbNow dbms         = error $ "Datasource " <> cs dbms <> " not supported"
+
+selectValue :: (PersistField a) => Text -> Transaction [a]
+selectValue sql = fmap unSingle <$> rawSql sql []
+
+now :: MonadTransaction m => m UTCTime
+now = runTrans selectNow
diff --git a/src/Yam/Transaction/Sqlite.hs b/src/Yam/Transaction/Sqlite.hs
new file mode 100644
--- /dev/null
+++ b/src/Yam/Transaction/Sqlite.hs
@@ -0,0 +1,22 @@
+{-# LANGUAGE DataKinds         #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+module Yam.Transaction.Sqlite where
+
+import           Yam.Import
+import           Yam.Logger.MonadLogger
+import           Yam.Transaction
+
+import           Data.Default
+import           Database.Persist.Sqlite
+
+
+data SQLite
+
+instance HasDataSource SQLite where
+  connector _ logger ds a = runLoggingT (withSqlitePool (conn ds) (toThread ds) (lift.a)) logger
+    where toThread ds | conn ds == conn def = 1
+                      | otherwise           = thread ds
+
+sqliteProvider :: MonadTransaction m => DataSourceProvider m a
+sqliteProvider = ("sqlite", connector (Proxy :: Proxy SQLite))
diff --git a/yam-app.cabal b/yam-app.cabal
new file mode 100644
--- /dev/null
+++ b/yam-app.cabal
@@ -0,0 +1,54 @@
+name:                yam-app
+version:             0.1.0.0
+synopsis:            Yam App
+description:         Base Module for Yam
+homepage:            https://github.com/leptonyu/yam/yam-app#readme
+license:             BSD3
+license-file:        LICENSE
+author:              Daniel YU
+maintainer:          i@icymint.me
+copyright:           Copyright: (c) 2017 Daniel YU
+category:            Web
+build-type:          Simple
+extra-source-files:  README.md
+cabal-version:       >=1.10
+
+library
+  hs-source-dirs:      src
+  exposed-modules:     Yam.App
+                     , Yam.Import
+                     , Yam.Transaction
+                     , Yam.Transaction.Sqlite
+                     , Yam.Logger.MonadLogger
+                     , Yam.Logger.WaiLogger
+                     , Yam.Event
+                     , Yam.Logger
+                     , Yam.Prop
+  other-modules:       Yam.App.Context
+  build-depends:       base >= 4.7 && < 5
+                     , monad-control
+                     , transformers
+                     , aeson
+                     , yaml
+                     , text
+                     , string-conversions
+                     , exceptions
+                     , time
+                     , random
+                     , data-default
+                     , unordered-containers
+                     , containers
+                     , ctrie
+                     , directory
+                     , fast-logger
+                     , monad-logger
+                     , wai-logger
+                     , persistent
+                     , resource-pool
+                     , reflection
+                     , persistent-sqlite
+  default-language:    Haskell2010
+
+source-repository head
+  type:     git
+  location: https://github.com/leptonyu/yam-app
