Skip to content

Instantly share code, notes, and snippets.

@MatejBransky
Last active April 3, 2022 21:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MatejBransky/75ba4cd8a89a839abfea37b81b9dd907 to your computer and use it in GitHub Desktop.
Save MatejBransky/75ba4cd8a89a839abfea37b81b9dd907 to your computer and use it in GitHub Desktop.
Clean Architecture (Domain Driven Design)

Clean Architecture

Domain Layer

At the center is the domain layer. It is the entities and data that describe the subject area of the application, as well as the code to transform that data. The domain is the core that distinguishes one application from another.

You can think of the domain as something that won't change if we move from React to Angular, or if we change some use case. In the case of the store, these are products, orders, users, cart, and functions to update their data.

The data structure of domain entities and the essence of their transformations are independent from the outer world. External events trigger domain transformations, but do not determine how they will occur.

The function of adding an item to cart doesn't care how exactly the item was added: by the user himself through the “Buy” button or automatically with a promo code. It will in both cases accept the item and return an updated cart with the added item. [A01]

Application Layer

Around the domain is the application layer. This layer describes use cases, i.e. user scenarios. They are responsible for what happens after some event occurs.

For example, the “Add to cart” scenario is a use case. It describes the actions that are should be taken after the button is clicked. It's the kind of “orchestrator” that says:

  • go to the server, send a request;
  • now perform this a domain transformation;
  • now redraw the UI using the response data.

Also, in the application layer theree are ports—the specifications of how our application wants the outside world to communicate with it. Usually a port is an interface, a behavior contract.

Ports serve as a “buffer zone” between our application's wishes and the reality. Input Ports tell us how the application wants to be contacted by the outside world. Output Ports say how the application is going to communicate with the outside world to make it ready. [A01]

Adapters Layer

The outermost layer contains the adapters to external services. Adapters are needed to turn incompatible APIs of external services into those compatible with our application's wishes.

Adapters are a great way to lower the coupling between our code and the code of third-party services. Low coupling reduces needs to change one module when others are changed.

Adapters are often divided into:

  • driving—which send signals to our application;
  • driven—which receive the signals from our application.

The user interacts most often with driving adapters. For example, the UI framework's handling of a button click is the work of a driving adapter. It works with the browser API (basically a third-party service) and converts the event into a signal that our application can understand.

Driven adapters interact with the infrastructure. In the frontend, most of the infrastructure is the backend server, but sometimes we may interact with some other services directly, such as a search engine.

Note that the farther we are from the center, the more “service-oriented” the code functionality is, the farther it is from the domain knowledge of our application. This will be important later on, when we decide which layer any module should belong to. [A01]

Shared Kernel

In practice, the shared kernel can be explained like this. We use TypeScript, we use its standard type library, but we don't consider them as dependencies. This is because the modules that use them may not know anything about each other and remain decoupled.

Not all code can be classified as shared kernel. The main and most important limitation is that such code must be compatible with any part of the system. If a part of the application is written in TypeScript and another part in another language, the shared kernel may contain only code that can be used in both parts. For example, entity specifications in JSON format are fine, TypeScript helpers are not.

Links

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment