graninas / Hydra

Hydra is a full-fledged framework for building web services, multithreaded and concurrent applications with SQL and KV DB support.
BSD 3-Clause "New" or "Revised" License
193 stars 13 forks source link

Hydra Framework

All-in-one framework for writing Haskell apps which use the following features out of the box:

With the framework, you can create complex applications which have a good maintainability, testability, simplicity, have a good structure and are easy to read and change. The key principles of the framework:

The Hydra Project

This project demonstrates the principles of Software Design and Architecture in pure Functional Programming. Hydra has several separate frameworks based on different engines for easy comparison:

The core idea of the two Free monadic frameworks is known as Hierarchical Free Monads.

The project is a showcase for my book Functional Design and Architecture. The approaches presented in Hydra are well-described and rationalized in the book, so you may obtain even more info about best practices and ideas of how to write a good Haskell code.

Note The Free monad based framework is the most developed by functionality. I'm working on synchronizing the functionality between all the engines.

Building dependencies

Ubuntu:

$ sudo apt-get install libpq-dev librocksdb-dev

MacOS:

brew install rocksdb postgresql

Building and running

Use stack for building all the framework and apps:

$ stack build

You can also switch the optimizations off and use several threads for building:

$ stack build --fast -j4

Running a project is also simple:

$ stack exec labyrinth

To load a subproject into GHCi, use the following command:

$ stack ghci labyrinth:exe:labyrinth

Sample applications

There are several sample applications:

Code samples

Sample SQL-related code

createMeteor :: MeteorTemplate -> D.SqlConn BS.SqliteM -> L.AppL MeteorId
createMeteor mtp@(MeteorTemplate {..}) conn = do
  L.logInfo $ "Inserting meteor into SQL DB: " <> show mtp

  let time = Time.UTCTime (toEnum 1) (Time.secondsToDiffTime 0)

  doOrFail
    $ L.scenario
    $ L.runDB conn
    $ L.insertRows
    $ B.insert (SqlDB._meteors SqlDB.astroDb)
    $ B.insertExpressions
          [ SqlDB.Meteor B.default_
            (B.val_ size)
            (B.val_ mass)
            (B.val_ azimuth)
            (B.val_ altitude)
            (B.val_ time)
          ]

  let predicate meteorDB
          = (SqlDB._meteorSize meteorDB     ==. B.val_ size)
        &&. (SqlDB._meteorMass meteorDB     ==. B.val_ mass)
        &&. (SqlDB._meteorAzimuth meteorDB  ==. B.val_ azimuth)
        &&. (SqlDB._meteorAltitude meteorDB ==. B.val_ altitude)

  m <- doOrFail
    $ L.scenario
    $ L.runDB conn
    $ L.findRow
    $ B.select
    $ B.limit_ 1
    $ B.filter_ predicate
    $ B.all_ (SqlDB._meteors SqlDB.astroDb)
  pure $ SqlDB._meteorId $ fromJust m

Additional materials

Checkout the following materials to learn more about he Hierarchical Free Monads approach used in Hydra: