Bring Domain-Driven Organization to your Codebase Today. Break Nothing.
As some of you may know, for several years now I have been interested in bringing domain holism to software projects.
App frameworks like Rails, Django, and Express know next to nothing about the domain of your application. With the right definitions you can reasonably argue that they do in fact know nothing, but my own view is a bit more generous.
Frameworks like the aforementioned are widely used to help developers build software better, faster, or more cheaply. In a perfect world all three, but we know how that goes.
What these app frameworks can’t and shouldn’t know are the nitty-gritty details of of your domain operations. And yet for whatever reason, these frameworks too-often ship with a very opinionated notion of how a project should be organized (hint: it usually aien’t “First understand your application’s domain — after that, its organization will be clear”).
Perhaps needless to see, a very opinionated idea in the right context can be priceless. But organizing the physical structure of your application exclusively around whatever app framework you are using — rather than around your application’s domain — seems to me to be shortsighted.
Take this pretty vanilla organization for a Rails API:
You may have seen this — or something like it — many times before. Not bad, eh? Everything is discretely tucked away in a drawer that says what it is. Isn’t it nice that Rails makes it so easy for us do things this way, and difficult to deviate?
Well…for knowing what to expect in broad strokes from one Rails project to the next, yes it’s very nice to have an organizational standard. It’s also nice to be able to see all modules in a category next to each other — see if they adhere to the same conventions, where good abstractions may lay dormant, and so on.
But I would argue that great codebases don’t just make changes simpler, they actively teach and deepen understanding of the application’s domain. If we haven’t worked in Rails before, or if we’ve never written software in a web development context, the organization you see above actually does have quite a bit to teach us about Rails. But if you ignore the commit messages and focus on the folder structure, ask yourself this — do you know anything, anything at all, about what this app does?
By the metric of teaching the application domain, this method of organization is clearly deficient. This is why many of us, when coming to a project, don’t rely on it for getting our footing — we go to the tests or the docs (if we’re lucky), the models, the routes; anywhere we can get a conceptual toehold.
We can do better!
A simple solution
Let me give you something you can run with today:
You can commit symbolic links to a git repository.
If you already knew that, you probably know where I’m going next. If I have a Rails API that I want to organize according to its entities, all I have to do is decide where I want to put the entity-based organization (I’m partial to
domain/entities) and start creating entity directories and links. If I have a
User controller, model, view, and tests, all I have to do is create relative symlinks to these in my
User entity directory. Same is true if I have a
User-oriented service, mailer, routes (if divided into separate files) etc. I’ve had Sorbet complain when adding it to a project with symlinks, but the workaround was simple (delete the directory of symlinks before init, then revert the change after).
Heck, if I’m working in a monorepo, I can also include the front-end code relevant to
User— having clear and immediate perspective of how an entity is represented across the stack is actually one of the major advantages of this approach.
This is already a lot of value — but wait, there’s more!
If I’m using Asciidoctor or reStructuredText for my documentation, I can use the include directive to place all of the entity-relevant code right there in an entity’s developer-facing documentation! I can even write a custom directive to auto-include all the files in the relevant entity directory!
You will have spied an additional maintenance burden associated with this technique, but consider that it needn’t be large. You can easily automate the detection if not outright correction of broken symlinks through your build process.
Get your yin without losing your yang
While adding this kind of domain organization to an existing project has been predominantly a thought experiment for me over the past few years, more recently I did finally start applying these techniques and uncovering their value.
One thing I want to explicitly say before closing — both types of project organization, the domain-oriented and the framework or category-oriented, have their own benefits. They are also both, in some sense or another, arbitrary. It is because both possess utility that adding another level of simulated organization to a project — a lens if you will—can make more sense than trying to make the much riskier changes frequently required to organize a framework-driven project according to its domain.
Have you tried this or a similar approach to project organization? Let me know in the comments!