Rails missing architecture

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.

Interesting take. Will probably comment a bit more after reviewing your example code further. Here’s my initial reaction though:

I’m thankful for the opinionated canvas which is our current Rails architecture – and also thankful that it allows for a wide variety of choices about adding new things – helpers / concerns / the “lib” folder / etc.

Personally I do prefer to put oodles of business logic into model classes, and feel that it is testable. But others on my team want all their logic apart from our core models. It works – we get along. To each their own really!