CMS on it's basis is a software that resolve one generic problem - managing content on website. Joomla, WordPress and other popular systems are simple and generic, there is no domain there - just creating posts, showing them at partcular URL... But, are You sure?
When I started writing own CMS, I decided to try to do this using Domain Driven Design. At the beginning it was really painful, because of the mindset that was set by popular systems. Now, after research, hours of modelling and prototyping, I changed my mindset. Let me show You how I've done that.
From this article You will find out:
- Where is Domain in CMS?
- How to change thinking to find Domain in CMS?
- How to crystalize behaviors from CMS?
- Does every CMS needs a DDD?
This article assumes You have basic knowledge of DDD and software architecture. Some parts of the research and analysis of Domain will be ommited here, to allows author create short but contentfull article.
Where is the Domain in CMS?
The basic functionality of CMS is to create some content and show it in browser. To create content You need some form and controller that handles that request, and saves data in database. CRUD right? But, there is a catch - what If You could describe "how system behaves" instead of how it's build or where it stores data? System creates content, publish this content upon URL, assigns content to category etc. So, what not just think of how system behaves instead of where to save the data?
Every software system is built to provide some solution to business problem. CMS is same thing - here the business problem is to allow users to add and modyfy content of the website. Just like in shop, You can "add product to cart" or "increase quantity of product in cart". In CMS You can "create new post" and "publish it".
Every behavior of the system is already a part of Your Domain. The point is, You have to think of any system in categories of behaviors, not technical things. From those behaviors You can find out the Bounded Contexts, new Aggregates came up as new flowers on spring :)
Yes, You are right, not everywhere :) Part of Our job as an architects is to decide where, in system, will be better to use DDD, and where simple CRUD. When You have 'save node', 'update node' "behaviors" - You don't need DDD :)
The better way to find the "domain" is a Event Storming - it is a powerfull modelling method. But it works only when You can do the session with at least Your one coleague - doing sessions alone is a terrible idea :D
DDD in CMS in real project
To show You real project, I need to describe You the business logic that stands upon that project. I'll show You my way of doing this in the Tulia CMS.
Domain logic of Tulia CMS
The content elements are called Nodes. Those Nodes can be translatable to foreign languages, can has attribites that store simple details of the Node (for example: image, some links, content of the node etc.). Nodes can be assigned and unassigned to categories. Nodes has titles and slugs, that are generated from the titles. Nodes also have purposes that describes what for this node was created, and one Node can have imposed multiple purposes. Ok, let's move on.
From the description above You can pick behaviors that describe the system. We have:
- create node
- add/update/remove attribute
- assign/unassign category
- change title
- impose/discontinue purpose
Aggregate
We have behaviors, so we can create aggregate! For this article I select only the Node behaviors, so is simpler to find the Aggregate, but always be aware of the "Aggregate rules" when You create it!
final class Node {
public static function create(
string $id,
string $title,
array $locales,
): self;
public function changeTitle(
string $title,
?string $alias = null,
): void;
public function addAttribute(
string $code,
mixed $value,
): void;
public function publish(
ImmutableDateTime $publishedAt,
?ImmutableDateTime $publishedTo = null,
): void;
/** Replace all purposes with new ones **/
public function persistPurposes(
string ...$purposes,
): void;
public function assignToMainCategory(
string $category,
): void;
}
Above code is a simplified version of real life Aggregate, that You can see here: https://github.com/tuliacms/cms/blob/master/src/Cms/Node/Domain/WriteModel/Model/Node.php.
The Node has title, attributes, category and purposes. As You might notice, there is no "content" - because content in Tulia CMS is a one of the Attributes. Static factory method is used to create objects in Tulia CMS, and $locales store all available locales in system (remember? Node is translatable).
The most important lesson here, there are no "technical things" here, such save/update, setters etc. - only behaviors. This is how system behaves, this is how system allows users to do anything with it. It helps You to find the Domain in any business problem, even so generic as CMS. You just have to stop thinking about things as technical system and force Yourself to think about the systems behaviors.
Does every CMS has a Domain?
Yes. Every system, because business has own Domain, is specialized in something. Not even CMS but every system, has some domain. Every system that solves any business problem has a domain, but question is... Does every CMS needs a DDD?
Does every CMS needs a DDD?
No, not every. If You build system that is simple, have only CRUD-like operations, or is way too generic (like reusable component for example), You do not need a DDD here. DDD affects of how much the code You need to write, and it's not worth it.
But, if Your system has specific behaviors, like in Tulia CMS, if exposes places where developers can extend this behavior - this is a good way to think about DDD here.
Conclusion
Every business-specific problem can be modelled with DDD, only if is not a CRUD :) Developing Tulia CMS was a really different approach to modelling system with DDD, because it wasn't obvious at first look where the domain in CMS is. When I changed my thinking to behaviour of system, everything changed.
If You are interested in how the whole domain od Tulia CMS is made, feel free to go here: https://github.com/tuliacms/cms/tree/master/src/Cms/Node. You can find whole Domain Model, with Aggregates, relationships between Entities, separation to Read Model and Write Model, and much more.
Top comments (0)