23

How does the pattern of using command handlers to deal with persistence fit into a purely functional language, where we want to make IO-related code as thin as possible?


When implementing Domain-Driven Design in an object-oriented language, it's common to use the Command/Handler pattern to execute state changes. In this design, command handlers sit on top of your domain objects, and are responsible for the boring persistence-related logic like using repositories and publishing domain events. The handlers are the public face of your domain model; application code like the UI calls the handlers when it needs to change domain objects' state.

A sketch in C#:

public class DiscardDraftDocumentCommandHandler : CommandHandler<DiscardDraftDocument>
{
    IDraftDocumentRepository _repo;
    IEventPublisher _publisher;

    public DiscardDraftCommandHandler(IDraftDocumentRepository repo, IEventPublisher publisher)
    {
        _repo = repo;
        _publisher = publisher;
    }

    public override void Handle(DiscardDraftDocument command)
    {
        var document = _repo.Get(command.DocumentId);
        document.Discard(command.UserId);
        _publisher.Publish(document.NewEvents);
    }
}

The document domain object is responsible for implementing the business rules (like "the user should have permission to discard the document" or "you can't discard a document that's already been discarded") and for generating the domain events we need to publish (document.NewEvents would be an IEnumerable<Event> and would probably contain a DocumentDiscarded event).

This is a nice design - it's easy to extend (you can add new use cases without changing your domain model, by adding new command handlers) and is agnostic as to how objects are persisted (you can easily swap out an NHibernate repository for a Mongo repository, or swap a RabbitMQ publisher for an EventStore publisher) which makes it easy to test using fakes and mocks. It also obeys model/view separation - the command handler has no idea whether it's being used by a batch job, a GUI, or a REST API.


In a purely-functional language like Haskell, you might model the command handler roughly like this:

newtype CommandHandler = CommandHandler {handleCommand :: Command -> IO Result)
data Result a = Success a | Failure Reason
type Reason = String

discardDraftDocumentCommandHandler = CommandHandler handle
    where handle (DiscardDraftDocument documentID userID) = do
              document <- loadDocument documentID
              let result = discard document userID :: Result [Event]
              case result of
                   Success events -> publishEvents events >> return result
                   -- in an event-sourced model, there's no extra step to save the document
                   Failure _ -> return result
          handle _ = return $ Failure "I expected a DiscardDraftDocument command"

Here's the part I'm struggling to understand. Typically, there'll be some sort of 'presentation' code which calls into the command handler, like a GUI or a REST API. So now we have two layers in our program which need to do IO - the command handler and the view - which is a big no-no in Haskell.

As far as I can make out, there are two opposing forces here: one is model/view separation and the other is the need to persist the model. There needs to be IO code to persist the model somewhere, but model/view separation says that we can't put it in the presentation layer with all the other IO code.

Of course, in a "normal" language, IO can (and does) happen anywhere. Good design dictates that the different types of IO be kept separate, but the compiler doesn't enforce it.

So: how do we reconcile model/view separation with the desire to push IO code to the very edge of the program, when the model needs to be persisted? How do we keep the two different types of IO separate, but still away from all the pure code?


Update: The bounty expires in less than 24 hours. I don't feel that either of the current answers has addressed my question at all. @Ptharien's Flame's comment about acid-state seems promising, but it's not an answer and it's lacking in detail. I'd hate for these points to go to waste!

4
  • 1
    Perhaps it would be helpful to look at the design of various persistence libraries in Haskell; in particular, acid-state seems to be close to what you are describing. Mar 1, 2014 at 19:04
  • 1
    acid-state looks pretty great, thanks for that link. In terms of API design it still seems to be bound to IO; my question is about how a persistence framework fits into a larger architecture. Do you know of any open-source applications that use acid-state alongside a presentation layer, and succeed in keeping the two separate? Mar 9, 2014 at 23:45
  • The Query and Update monads are pretty far removed from IO, actually. I'll try to give a simple example in an answer. Mar 10, 2014 at 5:10
  • At the risk of being off-topic, for any readers who are using the Command/Handler pattern this way, I really recommend checking out Akka.NET. The actor model feels like a good fit here. There's a great course for it on Pluralsight. (I swear I'm just a fanboy, not a promotional bot.)
    – RJB
    Aug 23, 2015 at 15:58

4 Answers 4

8
+50

The general way to separate components in Haskell is through monad transformer stacks. I explain this in more detail below.

Imagine we're building a system that has several large-scale components:

  • a component that talks with the disk or database (submodel)
  • a component that does transformations on our domain (model)
  • a component that interacts with the user (view)
  • a component that describes the connection between view, model, and submodel (controller)
  • a component that kickstarts the whole system (driver)

We decide that we need to keep these components loosely coupled in order to maintain good code style.

Therefore we code each of our components polymorphically, using the various MTL classes to guide us:

  • every function in the submodel is of type MonadState DataState m => Foo -> Bar -> ... -> m Baz
    • DataState is a pure representation of a snapshot of the state of our database or storage
  • every function in the model is pure
  • every function in the view is of type MonadState UIState m => Foo -> Bar -> ... -> m Baz
    • UIState is a pure representation of a snapshot of the state of our user interface
  • every function in the controller is of type MonadState (DataState, UIState) m => Foo -> Bar -> ... -> m Baz
    • Notice that the controller has access to both the state of the view and the state of the submodel
  • the driver has only one definition, main :: IO (), which does the near-trivial work of combining the other components into one system
    • the view and submodel will need to be lifted into the same state type as the controller using zoom or a similar combinator
    • the model is pure, and so can be used without restriction
    • in the end, everything lives in (a type compatible with) StateT (DataState, UIState) IO, which is then run with the actual contents of the database or storage to produce IO.
4
  • 1
    This is excellent advice, and exactly what I was looking for. Thanks! Mar 12, 2014 at 8:21
  • 2
    I'm digesting this answer. Please could you clarify the role of the 'submodel' in this architecture? How does it "talk with the disk or database" without performing IO? I'm particularly confused about what you mean by "DataState is a pure representation of a snapshot of the state of our database or storage". Presumably you don't mean to load the whole database into memory! Mar 12, 2014 at 13:51
  • 1
    I would absolutely love to see your thoughts on a C# implementation of this logic. Don't suppose I can bribe you with an upvote? ;-)
    – RJB
    Aug 23, 2015 at 17:03
  • 1
    @RJB Unfortunately, you would have to bribe the C# development team to allow higher kinds in the language, because without them this architecture falls a bit flat. Aug 23, 2015 at 17:07
5

So: how do we reconcile model/view separation with the desire to push IO code to the very edge of the program, when the model needs to be persisted?

Should the model need to be persisted? In many programs, saving the model is required because the state is unpredictable, any operation could mutate the model in any way, so the only way to know the state of the model is to access it directly.

If, in your scenario, the sequence of events (commands that have been validated and accepted) can always generate the state, then it's the events that need to be persisted, not necessarily the state. The state can always be generated by replaying the events.

Having said that, often times the state is stored, but just as a snapshot/cache to avoid replaying the commands, not as essential program data.

So now we have two layers in our program which need to do IO - the command handler and the view - which is a big no-no in Haskell.

After the command is accepted, the event is communicated to two destinations (the event storage, and the reporting system) but at the same layer of the program.

See Also
Event Sourcing
Eager Read Derivation

3
  • 2
    I'm familiar with event-sourcing (I'm using it in my example above!), and to avoid splitting hairs I'd still say that event-sourcing is an approach to the problem of persistence. In any case, event sourcing doesn't obviate the need to load up your domain objects in the command handler. The command handler doesn't know whether the objects came from an event stream, an ORM, or a stored procedure - it just gets it from the repository. Mar 1, 2014 at 12:37
  • 1
    Your understanding seems to couple the the view and the command handler together to create multiple IO. My understanding is that the handler generates the event and has no further interest. The view in this instance functions as a separate module (even if technically in the same application), and is not coupled to the command handler.
    – FMJaguar
    Mar 1, 2014 at 13:11
  • 1
    I think we might be talking at cross purposes. When I say 'view' I'm talking about the whole presentation layer, which may be a REST API, or a model-view-controller system. (I do agree that the view should be decoupled from the model in the MVC pattern.) I basically mean "whatever calls into the command handler". Mar 1, 2014 at 13:32
2

You're trying to put space into your IO intensive application for all the non-IO activities; unfortunately typical CRUD apps like you talk about do little other than IO.

I think you understand the relevant separation fine, but where you're trying to place the persistence IO code at some number of layers away from the presentation code, the general fact of the matter is in your controller somewhere you should be calling out to your persistence layer, which may feel too close to your presentation to you - but that's just a coincidence in that type of app has little else to it.

Presentation and persistence make up basically the entirety of the type of app I think you're describing here.

If you think in your head about a similar application that had a lot of complex business logic and data processing in it, I think you'll find yourself able to imagine how that is nicely separated away from the presentational IO and persistence IO stuff such that it needs know nothing about either. The problem you have right now is just a perceptual one caused by trying to see a solution to a problem in a type of application that doesn't have that problem to begin with.

6
  • 1
    You're saying that it's OK for CRUD systems to couple persistence and presentation. This seems reasonable to me; however I didn't mention CRUD. I'm specifically asking about DDD, where you have business objects witch complex interactions, a persistence layer (command handlers) and a presentation layer on top of that. How do you keep the two IO layers separate while maintaining a thin IO wrapper? Mar 11, 2014 at 8:55
  • 1
    NB, the domain I described in the question could be very complex. Perhaps discarding a draft document is subject to some involved permissions checking, or multiple versions of the same draft might need to be dealt with, or notifications need to be sent, or the action needs approval by another user, or drafts go through a number of lifecycle stages before finalisation... Mar 11, 2014 at 8:58
  • 2
    @BenjaminHodgson I would strongly advise against mixing DDD or other inherently OO design methodologies into this situation in your head, it's only going to confuse. While yes you can create object like bits and bobbles in pure FP, the design approaches based on them should not necessarily be your first reach. In the scenario you describe I would envision as I mention above, a controller that communicates between the two IO and pure code: Presentation IO goes into and is requested from controller, controller passes things down to the pure sections, and to the persistence sections. Mar 11, 2014 at 16:08
  • 1
    @BenjaminHodgson you can imagine a bubble where all your pure code lives, with all the layers and fancifulness you may want in whatever design you appreciate. The entry point for this bubble is going to be a tiny piece I'm calling a "controller" (perhaps incorrectly) which does the communication between the presentation, persistence, and pure pieces. In this way your persistence knows nothing of presentation or pure and vice versa - and this keeps your IO stuff in this thin layer above the bubble of your pure system. Mar 11, 2014 at 16:11
  • 2
    @BenjaminHodgson this "smart objects" approach you speak of is inherently a bad approach for FP, the problem with smart objects in FP is they couple far too much and generalize far too little. You end up with data and functionality that's tied to it, wherein FP prefers that your data have loose coupling to functionality such that you can implement your functions to be generalized and they will then work across multiple types of data. Have a read of my answer here: programmers.stackexchange.com/questions/203077/203082#203082 Mar 11, 2014 at 16:15
1

As near as I can understand your question (which I may not, but thought I'd throw in my 2 cents), since you don't necessarily have access to the objects themselves, you need to have your own object database that self-expires over time).

Ideally the objects themselves can be enhanced to store their state so when they get "passed around", different command processors will know what they are working with.

If that isn't possible, (icky icky), the only way is to have some common DB-like key, that you can use to store the info in a store that is setup to be shareable between different commands -- and hopefully, "open up" the interface and/or the code so any other command writers will also adopt your interface on saving and processing meta-information.

In the area of file servers samba has different ways of storing things like access lists and alternate data streams, depending on what the host OS provides. Ideally, samba is being hosted on a file system provides extended attributes on files. Example 'xfs' on 'linux' -- more commands are copying extended attributes along with a file (by default, most utils on linux "grew up" w/o thinks like extended attributes).

An alternative solution -- that works for multiple samba processes from different users operating on common files (objects), is that if the file system doesn't support attaching the resource directly to the file as with extended attributes, is using a module that implements a virtual file-system layer to emulate extended attributes for samba processes. Only samba knows about it, but it has the advantage of working when the object format doesn't support it, but still works with diverse samba users (cf. command processors) who do some work on the file based on its previous state. It will store the meta information in a common database for the file system which helps in controlling the size of the database (and doesn't need expiration of entries unless the files are deleted) -- if samba is the only accessor (the common interface) to the files, then it can transparently provide the same features to files on any file system, that otherwise would be file-system format specific (similar to different formats of data representation when objects are done by different people or teams).

It may not be useful to you if you needed more information specific to the implementation you are working with, but conceptually, the same theory could be applied to both problem sets. So if you were looking for algorithms and methods to do what you want, that might help. If you needed more specific knowledge in some specific framework, then maybe not so helpful... ;-)

BTW -- the reason I mention 'self-expiring' -- is that its not clear if you know what objects are out there and how long they persist. If you have no direct way of knowing when an object is deleted, you'd have to trim your own metaDB to prevent it filling with old or ancient meta info that the users have long since deleted the objects for.

If you know when the objects are expired/deleted, then you are ahead of the game, and can expire it out of your metaDB at the same time, but it wasn't clear if you had that option.

Cheers!

2
  • 1
    To me, this seems like an answer to a totally different question. I was looking for advice regarding architecture in purely-functional programming, in the context of domain-driven design. Could you clarify your points please? Mar 10, 2014 at 22:38
  • You are asking about data persistence in a purely-functional programming paradigm. Quoting Wikipedia: " Purely functional is a term in computing used to describe algorithms, data structures, or programming languages that exclude destructive modifications (updates) of entities in the program's running environment." ==== By definition, data persistence is irrelevant and has no use to something that modifies no data. Strictly speaking there is no answer for your question. I was attempting a more loose interpretation of what you wrote.
    – Astara
    Mar 16, 2014 at 4:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.