1
0
mirror of https://github.com/Unleash/unleash.git synced 2024-12-28 00:06:53 +01:00
unleash.unleash/website/docs/contributing/ADRs/back-end/write-model-vs-read-models.md
Thomas Heartman 64a9caab30
fix: display 3 types of models image (#7885)
The link to the image was incorrectly formatted, leading to the image
being displayed as a link. This fixes it so that the image is displayed
inline.
2024-08-15 11:12:04 +02:00

81 lines
3.6 KiB
Markdown

---
title: "ADR: Write Model vs Read Models"
---
## Background
We keep solving 3 problems in each of the business modules:
* changing the state of the system through actions
* displaying UI
* asking other modules for information
In the past we had one pattern of `service + store` for handling all 3 of those. Store was responsible for changing state and returning UI data. Cross module collaboration
happened by calling services in other modules. While it was convenient and reduced the number of building blocks to learn, it also led to coupling between
services and overloaded stores - responsible for writing data, reading complex UI data and answering questions from other modules.
## Decision
To address these challenges categorize your problem as one of 3 models:
![3 types of models](/img/write-model-vs-read-models.png)
We have 3 types of models serving 3 different purposes:
* I need to take action that modifies a state of the system (Write Model)
Solution: Go through the Command stack aka the *Write Model*. *Application logic* with the use case/workflow/steps resides in the *Application Service*. Application service can delegate
business logic handling to the *Domain Model* if needed (only for complex subdomains like Change Requests). Simple domain logic (very few business if statements)
can be handled together with the application logic. Application service calls the store that is responsible for the *generic CRUD operations* serving the write model.
* I need to read data for UI display (External Read Model)
Solution: Expose *External Read Model* aka *View Model* that returns all the data in one query so that frontend doesn't have to ask multiple write models for data.
View model will typically join data across a few DB tables since most UI screens required more than one table of data. If we keep the *complex queries* for UI
in the external read model our stores can be generic and focused on the main table operations.
* I need information from another module to proceed with my module application logic (Internal Read Model)
Solution: Cross module queries can be handled with the *Internal Read Model*. Internal means it's used internally between our modules and not exposed to the UI.
Internal read models are typically narrowly focused on answering one question and usually require *simple queries* compared to external read models. By introducing internal
read model other business modules are not coupled to our module's service/store/write model and can't call write model methods from another module by accident.
## Example
Before (one multi-purpose class)
```typescript
class SegmentStore {
// used to perform actions on segment
create(segment: Segment): Promise<Segment> {}
get(id: number): Promise<Segment> {}
update(id: number, segment: Segment): Promise<Segment> {}
delete(id: number): Promise<void> {}
// used by UI
getAll(): Promise<SegmentWithUsageInfo[]> {}
// used by another module checking existing names
getSegmentNames(): Promise<string[]> {}
}
```
After (3 role-based classes so clients depends only on method they use)
```typescript
// used to perform actions on segment
class SegmentStore {
create(segment: Segment): Promise<Segment> {}
get(id: number): Promise<Segment> {}
update(id: number, segment: Segment): Promise<Segment> {}
delete(id: number): Promise<void> {}
}
// used by UI
class SegmentViewModel {
getAll(): Promise<SegmentWithUsageInfo[]> {}
}
// used by another module checking existing names
class SegmentReadModel {
getSegmentNames(): Promise<string[]> {}
}
```