Hi there,
(I originally posted this to Twitter and someone suggested it may fit here)
Rails is a fantastic web framework. The only core idea of Rails which I always stuggled with was where to put the business logic - the logic which doesn’t belong to models or even service objects. A logic which I can test on its own, outside of Rails, with no dependency to Rails.
This diagram shows a possible architecture which fits well with Rails, but is not really part of Rails anymore.
It has been tried on https://github.com/RailsEventStore/ecommerce if you want to see more code with it.
All the ideas are not innovative - they come from Domain Driven Design, Event-Driven Architecture and CQRS. It’s just nicely combined with Rails here.
I see Rails and those things as complementary combo for more complex business apps on Rails.
Here is what you can take from this diagram:
1. App layer vs domain layer
Assume that controllers and service objects are app layer (a bit simplification here, but bear with me).
Service objects can never call each other. They are the application facade.
2. App layer modularisation is based on what “apps” you have
Usually you have some public facing app, admin panel, mobile API, some integrations.
Those are good candidates for “modules” at the app layer. Don’t confuse it with domain layer.
3. App layer has different modularisation than domain layer
This is the biggest trap I’ve seen in Rails apps. Trying to have one dominant modularisation, glueing app layer and domain layer.
4. Have a clear domain interface
I like to use commands as the domain interface. Simple data structures, smallest possible - which reflect the intent of what needs to be changes in a specific domain module
5. Domain modules are your business departments
If you know #domaindrivendesign you know what I mean. Those are bounded contexts.
It’s actually more common than not, that many projects have almost the same domain modules. We all have prices in our apps, we have invoices, we have inventory/availability.
6. Denormalize your view data with read models
In Rails apps this is the biggest win usually.
But it’s also the hardest one to convince people to.
But this is where you gain most in terms of performance.
Also, this allows clear testable view modules.
7. Domain modules output is events
Events are simple data structures. This is what has happened.
8. Processes coordinate domains
Domains alone can’t do much. They are too specific.
Processes are the thing that coordinates them.
Processes are like business checklists. When A, B, C happens do X.
Where A,B,C are events and X is a command.
9. This architecture allows for real unit testing
This is where Rails apps struggle. There is no modularisation.
So devs treat each class as a unit. Which is wrong.
This architecture puts clear rules on the modules.
App layer turns params into commands. Domain modules turn commands into events. Processes turn events into commands. Read modeles turn events into data.
10. Audit log
If every change is captured as an event, then we have an audit log for free.
Debugging with such audit logs is much easier.
(Bonus, optional)
11. Event Sourcing as persistence
This one is optional, but worth trying with this architecture. Instead of persisting your objects with their last state, we can build our objects from events.