DEV Community

Cover image for Domain Driven Design Implementation Plainly
Arthur Groupp
Arthur Groupp

Posted on

Domain Driven Design Implementation Plainly

The idea of the Domain Driven Design in Angular perfectly presented and fully explored by Manfred Steyer in his DDD series. I won’t rewrite here all the theory and will leave it to your own revision of that great work. In this article, I will show my vision of its implementation with Nx-based monorepo.

Law and order

The main idea is to divide your application by the self-contained parts that we are going to call domains.

As the result, we will have organized structure instead of pile of libraries. Every domain will have the libraries inside it to serve its purpose. From now on, at least two tags will accompany every new generated library: domain and type. As you already understood, the domain tag will hold the domain name this library belongs to, and the type will label the category of the library. I suggest using these kinds of categories:

Category Description Allowed dependencies
domain-logic Main logic of the domain. Contains of services, stores and entities data structures. Must provide façade services for maintaining encapsulation. util
feature Use case implementation. Contains of page and container components. Refers to domain-logic for data and calculations. ui, domain-logic, util
ui Collection of presentational components used in the domain features. util
util Collection of helper functions and classes. Usually it must be pure functions in separate file every, to improve the tree shaking functionality. n/a

To provide this strict dependencies allowance we must set these rules in .eslintrc.json in the root of the repository.

    ...,
    "@nrwl/nx/enforce-module-boundaries": [
        "error",
        {
            "enforceBuildableLibDependency": true,
            "allow": [],
            "depConstraints": [
              {
                "sourceTag": "type:app",
                "onlyDependOnLibsWithTags": ["type:feature", "type:ui", "type:domain-logic", "type:util", "type:data-access"]
              },
              {
                "sourceTag": "type:feature",
                "onlyDependOnLibsWithTags": ["type:ui", "type:domain-logic", "type:util"]
              },
              {
                "sourceTag": "type:ui",
                "onlyDependOnLibsWithTags": ["type:util"]
              },
              {
                "sourceTag": "type:domain-logic",
                "onlyDependOnLibsWithTags": ["type:util"]
              },
            ]
        }
    ],
    ...
Enter fullscreen mode Exit fullscreen mode

Domain or not domain

Must we create the domain for every functionality of the project? No. Domain is self-contained reusable part of the application that includes domain logic and at least one lazy loaded feature. There is no sense to create separate domain for every collection of service consumed by applications, it can be the standalone libraries or it can be domain named shared that will unite all these libraries with category data-access. In last case, we will need to add this category to linting rules.

The domain

Practically domain itself is a folder inside libs folder of monorepo. Inside this folder, we will collect all of the libraries belong to this domain.

So, let’s create one. To start new domain we need to create the library named domain inside directory with our new domain name. Let’s call it feature1:

$ nx g library domain --directory=feature1 --tags="domain:feature1,type:domain-logic"
Enter fullscreen mode Exit fullscreen mode

Congratulations, new domain named feature1 was born.

Now let’s create the library that will hold our features (lazy loaded pages and other container components):

$ nx g library features --directory=feature1 --tags="domain:feature1,type:feature"
Enter fullscreen mode Exit fullscreen mode

Let’s create page called page1 inside features:

$ nx g m page1 --routing --project=feature1-features
$ nx g component page1/page1 --flat --project=feature1-features
Enter fullscreen mode Exit fullscreen mode

This will create folder page1 inside feature1/src/lib with new module and container component called page1.

Now, when we have our first container component, it will apparently need some data, maybe API calls. Time to prepare it inside domain logic library.

Domain Logic

Domain logic (DL) library is a heart of our new domain. Unlike domain features, it usually doesn’t make sense to have more than one domain logic. The structure of DL supposed to include at least three folders: application, entities and infrastructure.

Folder name Description Is exported?
application Should hold façade services. I recommend creating separate façade service for every feature according to its needs to keep the principle of providing only the data customer demands. Definitely, if different features using similar data there is sense to use the same façade. Yes
entities Should hold interfaces, data classes, models, constants and injection tokens. The decision about exporting this folder depends on the demand of these data structures outside. Yes/No
infrastructure Should hold all calculations, data access services, guards, interceptors, stores and state management. I don’t recommend to export this folder, keep it as a private of the domain and provide access through the façade services. No

As an example, we’ll create one infrastructure service and one façade for our page1.

$ nx g service infrastructure/feature1 --project=feature1-domain
$ nx g service application/page1-facade --project=feature1-domain
Enter fullscreen mode Exit fullscreen mode

UI

UI library is the place where we are going to store our presentational components utilized by multiple features of the domain. It can’t be dependent on domain logic or features because neither service can be injected in presentational component. Additionally, this is the good place for Storybook.

I prefer to create every component with it’s own module in separate folder as ng-package. Let’s create UI library:

$ nx g library ui --directory=feature1 --tags="domain:feature1,type:ui"
Enter fullscreen mode Exit fullscreen mode

To be able to import separate packages unlike the whole ui library, we need to make correction of the tsconfig.base.json in the root folder of the repository:

paths: {
    "@<org-name>/feature1/ui/*": ["libs/feature1/ui/src/lib/*"]
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

The Domain Driven Design gives us perfect tool to bring an order into single page applications becoming every day more and more complex. It allows safely sharing the development process between different divisions and still having consistent application.

Of course, it adds much more work and boilerplates but it will be rewarded in future maintenance.

Photo by Sikai Gu on Unsplash

Top comments (5)

Collapse
 
fyodorio profile image
Fyodor

Solid approach with good SoC, thanks for sharing 👍

Though the domain-logic- structure looks a bit verbose to me. Why not decouple it to models-, store-, data-access- (or alike - for data access literally - API services and stuff like that - or even combining it with the store- - which makes total sense IMO)? The business-logic services can be placed inside corresponding feature- libs, to avoid traversing the whole folder tree searching for them.
nx is already too nested and verbose so deepening it on is too much for me.

It's just an opinion anyway, I saw a lot of nx projects and all of them are totally different indeed 😁 Though the domain-driven separation is almost always a good start 👍

Collapse
 
fkrautwald profile image
Frederik Krautwald

I think this article could benefit from using concrete domains (e.g., booking) and list the directory structure created. It’s hard to keep a mental model of generic words like feature, feature1, and page1 as they also mix with the type categories.

However, it’s nice to see DDD being applied to an Nx project.

Collapse
 
santoshyadavdev profile image
Santosh Yadav

Nice article 🎉

Collapse
 
mwoodpatrick profile image
Mark Wood-Patrick

Another good article but a concrete example on github would really add to this article

Collapse
 
muzammilaalpha profile image
muzammilaalpha

Keep sharing!!