Domain Driven Design Implementation Plainly
Tags: angular
Published: 2021-02-09 08:09:01

The idea of the Domain Driven Design in Angular perfectly presented and fully explored by [Manfred Steyer](https://twitter.com/ManfredSteyer) in his [DDD series](https://www.angulararchitects.io/aktuelles/sustainable-angular-architectures-1). 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.

```json
    ...,
    "@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"]
              },
            ]
        }
    ],
    ...
```


## 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*:

```sh
$ nx g library domain --directory=feature1 --tags="domain:feature1,type:domain-logic"
```

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):

```sh
$ nx g library features --directory=feature1 --tags="domain:feature1,type:feature"
```
Let’s create page called `page1` inside features:

```sh
$ nx g m page1 --routing --project=feature1-features
$ nx g component page1/page1 --flat --project=feature1-features
```
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.
```sh
$ nx g service infrastructure/feature1 --project=feature1-domain
$ nx g service application/page1-facade --project=feature1-domain
```


## 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](https://storybook.js.org/). 

I prefer to create every component with it’s own module in separate folder as `ng-package`. Let’s create UI library:
```sh
$ nx g library ui --directory=feature1 --tags="domain:feature1,type:ui"
```
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:
```json
paths: {
	"@<org-name>/feature1/ui/*": ["libs/feature1/ui/src/lib/*"]
}
```


## 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.

<span>Photo by <a href="https://unsplash.com/@gentle_kay?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Sikai Gu</a> on <a href="https://unsplash.com/s/photos/warehouse?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></span>