This is the first post in a short series about scaling a Domain Driven Design style architecture. Starting at the MVP stage through to profitable business.
The use case I will look at to begin with is sending email when a bid is placed in a fictitious auction website.
1 2 3 4 5 6 7 8 9
Our model has no validations. This is so we don’t restrict ourself with regards to what is considered a valid entity. It is pretty much a simple data object. The
from_form method is used later to initialize a new entity from a form object.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
We create a “form” with which to capture, validate and sanitise user input. This will be used to create a new
The Virtus gem provides us with an
attribute macro which coerces values to the correct type and an initializer which given a hash will set the attributes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
The service will take the
form and use it to create a new
Bid entity. It will broadcast an event, using Wisper, to signal the outcome.
While not shown here I use the
initialize method for injecting dependencies to make unit testing easier.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
This is the context which brings each part together. I’ve not included the view the user sees but you can imagine the use of
form_for to show a HTML form which the user can interact with.
Now imagine we want to send an email to the seller to notify them of the new bid.
We could put the mailing code directly in the service object. However I see email as a UI and not part of the core business logic, its on the outside of the Hexagon. So instead, in the controller, we can subscribe a listener to our service object which will react to the
1 2 3 4 5 6 7 8 9
1 2 3 4 5
It would be nice if we didn’t have to have a separate mailer class, but
ActionMailer makes this tricky. Typically I’d have the listener and mailer in the same file.
With this code we have a clear separation of concerns, the boundary and responsibility of each object is clear. The simplicity of each object means that they can be used in different contexts. In this case a web application, but equally a native app or REST API.
Objects either tell other objects what to do or tell them that something happened. They don’t ask for bits of state and act on them as this would create an unnecessary dependency on the internals of the other object.
I’ve nested everything related to the concept of ‘bids’ in a
Bids module, this is something like a bounded context in DDD and provides a neat way of later pulling out a particular concept in to its own service.