Ha, this is very interesting. I have been having similar feelings and did some experiments going towards this direction.
In my canse, I have a system where I have very complex permissions system. I have Users
that can have assigned many roles, and on top of that be granted excetpions to access parts of the Records
. The Records
themselves can have many Positions
and they have many more sub-sections/related tables in database.
The SQL query that finds records given user has access too is literally an A4 page of 12px print. It’s insane piece of SQL glued together, having a few layers of subqueries. In addition to that I need to answer the reverse question: which users have access to given record, and you may guess - it generates the opposite insanely crazy A4-page of SQL print. On top of that I have to keep these two queries in sync.
I did an experiment basically replacing my Records
with aggregates that are GenServer
behind the scenes. Not only I could replace the logic written in unreadable SQL with a nice set of function calls, but I improved the performance of the system by an order of magnitude (queries down from ~800ms to ~20ms).
Moreover, if we localize the updates to the records, we no longer have inserts/updates affecting the performance of the queries. We can also get rid of majority of datase-backed transactions and replace them with in-aggregate application-level transactions instead.
I think DDD is a natural way of building Erlang/Elixir apps. You may not even call it DDD. But look around the ecosystem and it’s happening all over the place. OTP gives us ready to use primitives: GenServers can be started as aggregates, we can hibernate them when not needed, persist state to DETS / mnesia / SQL database, shard naturally across the cluster. This is the way you can build more horizontally scallable system.
In my case the speed improvement is one thing, but I am also able to localize the queries/updates, allowing me to do more things at the same time in parallel. This is a big gain for a complex multi-tenant app that has a lot of data to process, but it can be sharded in a very nice way.
When you build an app that it’s whole global state is saved in SQL (or NoSQL) database, this is the one source of truth. This is the bottleneck, this is the place where a lot of convoluted logic lands in, this is the pain for scaling, updating, migrating the data.
Ont the other hand a system that is built around DDD principles, can have the “object-oriented” abstractions put in place: aggregates that are processes/genservers, communicate via message passing, do a lot of things in async fashion etc. At the same time you can very well keep the SQL database and use it in the way it was meant to: to generate reports, ad-hoc query the data, analyse it etc.
Ok that sort of got out of hand with the story above. But my point is: your approach is probably great choice. You will use the power of Erlang/OTP to help you scale the thing out, and you will end up having better system architecture at the same time.