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, invoices, inventory etc
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.
9+
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.
11. Event Sourcing (optional)
Instead of persisting your objects with their last state, we can build our objects from events.
I hate to make marketing based on the virus situation, but people keep asking me about async/remote work tips and most of the questions are answered in our (@arkency) "Async Remote" ebook.
1. someone writes a channel message (M) 2. I read it, interesting, but not much to add from me, yet 3. I go to focus on my task, turn off slack 4. other people add new messages to that channel, less related 5. the original message M is now one screen/scroll behind
6. someone comments on M by replying in a thread 7. they forget to check the "send to channel" (because it's not on by default and it's easy to forget) 8. original author replies to thread (again not to channel) 9. they keep exchanging interesting points