Skip to content

Commit

Permalink
docs: add cross boundary interaction adrs (#837)
Browse files Browse the repository at this point in the history
  • Loading branch information
timonmasberg authored Apr 28, 2024
1 parent d5b87eb commit fda9704
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# ADR010: Using NestJS Sagas for Cross-Domain Interactions (Reactive)

## Status

Accepted

## Context

As Kordis embraces loose coupling and an event-driven architecture, managing
interactions between bounded contexts is a critical topic. Traditional
approaches often involve direct integration between domains, leading to tight
coupling and reduced flexibility. The system requires a mechanism to react to
events from various domains and translate these events into appropriate actions
within a target domain without creating direct dependencies between them.

## Decision

We have decided to adopt NestJS Sagas to handle cross-domain interactions within
our system. NestJS Sagas will act as a mediator that listens to domain events
from various sources and translates them into domain-specific commands. This
approach leverages the strengths of NestJS's built-in support for event handling
and Sagas' ability to orchestrate complex sequences of operations across
different domains.

Implementation involves the following steps:

Listening to Events: Sagas will subscribe to events published by other domains
to the EventBus.<br> Data Enrichment and Transformation: Upon receiving an
event, the Saga may perform necessary queries to fetch additional data required
to process the event. It will also transform the event data into a format that
is understood by the target domain. You should use the
[rxjs operators](https://rxjs.dev/api/operators/) for this. <br> Command
Execution: The Saga then issues a command to the target domain, effectively
translating the event from the source domain into an actionable command in the
target domain.<br> By using Sagas in this manner, we create a layer that
abstracts the process of reacting to external events and invoking
domain-specific operations. This layer serves as the interface for interaction
with the domains.<br> <br> **The Saga implementation CAN live in the domain of
the target domain (the domain that receives the commands), since, as described,
we believe it acts as an interface. But, it should never be part of the domains
module and therefore never use specific providers. It should only communicate
via Commands and Queries, as the whole idea is, the Saga could live somewhere
else in the system, and it should not depend on the domain module.**

## Consequences

**Benefits:**<br> Decoupling: This approach significantly reduces the coupling
between domains, as they only need to emit events and listen to commands,
without knowing the internals of other domains.<br> Flexibility: It becomes
easier to add new sources of events or change the logic of how events are
processed, as these changes are encapsulated within the Sagas.<br>
Maintainability: By centralizing the logic for cross-domain interactions within
Sagas, the system becomes more maintainable. Each Saga can be independently
developed, tested, and modified, and in the future also independently
deployed.<br> <br> **Drawbacks:**<br> Complexity: Introducing Sagas adds a layer
of complexity to the system, requiring developers to understand the event-driven
model and the specifics of Saga implementation.<br> Debugging and Tracing:
Tracking the flow of events and commands through Sagas can be challenging,
especially in a system with many interacting domains. Effective logging and
tracing mechanisms are essential to mitigate this issue.<br> <br> Adopting
NestJS Sagas for cross-domain interactions presents a strategic approach to
building scalable, maintainable, and decoupled systems. The benefits in terms of
flexibility and maintainability outweigh the challenges associated with the
added complexity and debugging difficulties.
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# ADR010: Using Managers for Cross-Domain Interactions (Proactive)

## Status

Accepted

## Context

As Kordis embraces loose coupling and an event-driven architecture, managing
interactions between bounded contexts is a critical topic. Traditional
approaches often involve direct integration between domains, leading to tight
coupling and reduced flexibility. The system requires a mechanism to communicate
with multiple systems in order to manage cross domain related tasks. This is
analogous to the reactive part described in
[ADR009](./adr009-sagas-for-reactive-cross-domain-interactions.md), but the
difference is, that the system is not reacting to events, but proactively
communicating with other systems.

## Decision

We have decided to adopt the concept of Managers to handle cross-domain
interactions within our system. Managers will act as a mediator that
communicates with other systems and translates them into domain-specific
commands. By introducing a dedicated instance that communicates with each
domain, we decouple the domains from each other and create a layer that
abstracts the process of interacting with other domains. This layer serves as
the interface for interaction with the domains.

Implementation involves the following steps: <br> Process managers will be
implemented as a distinct library and NestJS module. The manager will follow the
Clean Architecture approach, just as it is implemented in every Domain Module.
The Manager is allowed to implement actual Controllers (e.g. GraphQL resolvers)
in order to react to incoming request from the frontend/other systems. The
manager is allowed to throw "Domain"-specific events and implement reactive
components to these events, such as GraphQL Subscription Handlers. The
interaction with Domains should be done via Commands and Queries, as the whole
idea is, the Manager could live somewhere else in the system, and it should not
depend on the domain module. The Manager must not use specific providers from
the domain module. This makes sure that we can outsource the Manager and Domains
and let them interact through a Bus.

## Consequences

**Benefits:**

Decoupling: This approach significantly reduces the coupling between domains, as
they only need to emit events and listen to commands, without knowing the
internals of other domains.

**Drawbacks:**

Complexity: Introducing Managers adds a layer of complexity to the system,
requiring developers to understand the specifics of Manager implementation.

Debugging and Tracing: Tracking the flow of events and commands through Managers
can be challenging, especially in a system with many interacting domains.
Effective logging and tracing mechanisms are essential to mitigate this issue.

0 comments on commit fda9704

Please sign in to comment.