vacationlabs / haskell-webapps

Proof-of-concept code for a typical webapp implemented in various Haskell libraries/frameworks
MIT License
134 stars 21 forks source link
haskell webapp

Current project status

We are invovling the community in this effort via a Haskell Internship. Please apply if you're interested. Contributions have already starting flowing in and you should read CONTRIBUTIONS if you're interested.

Request For Comments

Join the chat at https://gitter.im/haskell-webapps/Lobby

Any relevant full-featured libraries that have been missed? Any libraries that are de-facto standards (eg. Aeson for JSON) which can reduce the scope of this seemingly humongous task? Any library that has a known better alternative, for all practical purposes? Suggestions for how to make this effort a success? Suggestions to reduce the effort/scope, but still meet the overall goals of the effort?

Motivation

There is too much choice, with respect to libraries, when building a typical RDBMS-backed webapp. With probably the sole exception of Yesod, which is a pretty complete, and opinionated framework. Most people (especially newcomers to Haskell) don't know enough about advantages/disadvantages to make the right choice. Making the wrong choice leads to a lot of needless frustration

What kind of choices and what kind of decisions am I talking about? Sample the following:

I believe most of these things are possible in Haskell and its rich library ecosystem. However, making all of this work is not as easy as it ought to be. This is because the idiomatic and will-work for-80%-of-the-use-cases-with-20%-effort way of dealing with these things is not documented properly in one place. This gets more complicated due to the library fragmentation. Libraries make very different decision choices and take very different approaches for solving the same problem. Transliterating the idiomatic way from one library to the other may not result in the most pragmatic codebase.

While there are great tutorials (either provided by the project maintainers themselves or in various blog posts), my experience is that most tutorials walk you through the most basic scenarios. I wasn't able to easily find answers for most real-life scenarios. Asking on various forums like IRC (#haskell), /r/haskell, or StackOverflow, while helpful (the Haskell community is the most helpful and knowledgable community that I have come across) has the following problems:

Another motiviation (actually, the real motivation), is that I'm scratching my own itch. I run a SaaS company, Vacation Labs, which has a Rails+AngularJS codebase that has grown to 250,000+ LoC over the past 4 years. We're experiencing the disadvantages of using dynamically typed languages on a very large code-base. While automated tests help (unit tests & controller tests), they don't give enough correctness guarantees that something like Haskell can give. I wanted to quickly evaluate Haskell for our use-case, but going through the steep learning curve (functors, monads, laziness, purity, Reader, monad transformers, etc.) has taken a lot of calendar-days (I don't have too much free time these days). I'm not left with enough time to evaluate multiple libraries to pick the best. Therefore, I want to crowd-source the effort, generating a valuable community resource in the process.

The Plan

The very first step in the plan is to spec out a typical Postgres-backed web-app which covers all the points mentioned in the "Motivation" section. Here's what the spec will cover:

The next step will be to implement this webapp using various Haskell libraries/frameworks.

However, it won't make sense to implement the entire app (UI and API) for every possible combination of libraries. Therefore, the plan is to implement the spec in phases/parts/layers. The assumption is that the phases are loosely coupled and the library choices for one phase do not impact the other phase significantly. (This might be not be true in the cases of frameworks like Yesod, but then again, this is not supposed to be a scientifically controlled experiment).

Phase 1: Domain-level API + DB-access + Validations

  1. Low-level DB functions, DB<=>Haskell mapping, and house-keeping columns (createdAt, updatedAt)
    • Opaleye
    • Groundhog
    • Persistent
    • Postgresql-ORM
  2. Domain-level API with validations (which takes care of fetching records that may have DB-associations)
  3. Audit logging

Phase 2.1: JS-powered SPA

  1. Domain-API mentioned in the section above will be reused
  2. Write JSON-based API to access the domain API
    • Servant + Aeson
    • Yesod + Aeson
    • Snap + Aeson
  3. Write SPA in Haskell-powered technologies
    • GHCJS + Reflex-FRP
    • Any other?
  4. Redis caching for JSON responses from the API
    • TODO - is there a defacto standard library for Redis access?
    • TODO - is it possible to have a caching layer at the web-server level so that it doesn't have to be implemented by the app?

Phase 2.2: Server-powered HTML UI

  1. Domain-API mentioned in the section above will be reused
  2. Convert HTML into templates, and wire up the UI as per the spec:
    • Shakespearean templates
    • Lucid
    • Blaze
    • Heist
  3. Redis caching for the HTML responses from the server
    • TODO - is there a defacto standard library for Redis access?
    • TODO - is it possible to have a caching layer at the web-server level so that it doesn't have to be implemented by the app?

Phase 3: Testing

  1. Unit tests for domain-level API
    • Quicktest
    • Hspec
    • TODO - Anything else which is better for webapps?
  2. Integration/browser tests using Selenium
    • TODO - Which library?

Phase 4: Deplyoment

TODO - Which libraries are used for deploying Haskell webapps?

  1. Combining and minifying static assets
  2. Various cache-busting techniques available for ensuring stale assets are not fetched by browsers
  3. How to rollback a faulty deployment?

Phase 5: Supporting the app in production

  1. Implement specialized app-level logging
  2. Tweak web-server request/response logging to log in a customized format.
    • Scrub out sensitive data (like passwords or API secrets) in the request/response logs.
  3. How does the app log runtimes error in the hopefully rare instances of when they occur? Should one always deploy with profiling turned on? If not, how does one get stacktraces to help debug production errors?
  4. What if you have to hotpatch a code-fix? (like how it's down in the Common Lisp world)?
  5. Can one connect to a running Haskell intance and inspect its state?
  6. Configure a standard tool for remote error logging (eg. Airbrake, Scoutapp, New Relic, etc)
  7. Configure a standard tool for performance monitoring (eg. Skylight, Scoutapp, New Relic, etc)

The Spec

Spec available at SPEC

The idea is to implement few parts of a typical shopping cart. When specced out completely, a shopping cart, can be very large. However, the idea is neither to spec-out nor implement everything. The plan is to spec out (and implement) enough to cover the following functional areas: