-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
410316a
commit 29b87fb
Showing
43 changed files
with
576 additions
and
317 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
Empty file.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
# Architectural Approaches | ||
|
||
## Event-Sourcing | ||
|
||
**Event Sourcing** is an architectural pattern in which each change in the state of a system is recorded as a | ||
separate event. Instead of storing the current state of an object, the system stores the sequence of events that led to | ||
that state. When necessary, the current state is restored by "replaying" the events in the order in which they occurred. | ||
|
||
## CQRS | ||
|
||
**Command Query Responsibility Segregation (CQRS)**, is a design pattern that divides the task of managing commands and | ||
inquiries among several components. Separating | ||
the methods for reading and publishing data is the primary goal of the CQRS architectural pattern. It separates the read | ||
and update operations on a datastore into two separate models: Queries and Commands, respectively. | ||
|
||
### CQS vs CQRS | ||
|
||
Command Query Separation (CQS) and CQRS are related in that CQRS extends upon the fundamental concept of CQS. To put it | ||
simply, this is how they are related: | ||
|
||
* **CQS**: It is a programming principle that says you should separate operations that change data (commands) from those | ||
that read data (queries). If you have a method, for instance, it should either return something or update something, | ||
but not both. | ||
|
||
* **CQRS**: By dividing the design of the entire system into two sections—one for managing commands (writing or | ||
modifying data) and another for managing queries (reading data), CQRS expands on this idea. Each side can have its | ||
own database or model to optimize how they work. | ||
So, CQS is the basic rule, and CQRS is like an advanced version of it used for bigger systems where you want to handle | ||
reading and writing differently. | ||
|
||
## Vertical Slice | ||
|
||
**Vertical Slice** is an architectural pattern that suggests that each feature should be implemented in its own slice | ||
of the architecture. This means that each feature should have its own set of classes, including controllers, services, | ||
and repositories. This pattern is useful for large applications that have many features, as it helps to keep the | ||
codebase organized and maintainable. | ||
|
||
## References | ||
|
||
* [Implementing Domain-Driven Design Cheat Sheet](https://www.linkedin.com/pulse/implementing-domain-driven-design-cheat-sheet-saeed-farahi-mohassel/) | ||
* [Repository - DDD in Ruby on Rails](https://www.visuality.pl/posts/repository-ddd-in-ruby-on-rails) | ||
* [Многоликий DDD - YouTube](https://www.youtube.com/watch?v=NSN-NXfbEqM) | ||
* [Паттерн CQRS — руководство для чайников - Tproger](https://tproger.ru/articles/cqrs-dlya-chajnikov) | ||
* [CQRS – Command Query Responsibility Segregation Design Pattern - GeeksForGeeks](https://www.geeksforgeeks.org/cqrs-command-query-responsibility-segregation/) |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,234 @@ | ||
# Enterprise Design Patterns | ||
|
||
## Table of contents | ||
|
||
* [Value Object](#value-object) | ||
* [Domain Model](#domain-model) | ||
* [Aggregate](#aggregate) | ||
* [Data Mapper](#data-mapper) | ||
* [Active Record](#active-record) | ||
* [Data Access Object](#data-access-object) | ||
* [Repository](#repository) | ||
* [Specification](#specification) | ||
* [Identity Map](#identity-map) | ||
* [Unit of Work](#unit-of-work) | ||
* [Sources](#sources) | ||
|
||
## Value Object | ||
|
||
**Value Object** is an enterprise design pattern that is used to represent objects that are characterized by their state | ||
(value) rather than identity. This pattern is used to model simple entities that have a value | ||
and do not have an identity of their own. | ||
|
||
### Properties | ||
|
||
* **Value-based**: Two objects are considered equal if all their attributes are equal. | ||
* **Immutability**: Value Objects are usually immutable once created. | ||
* **Modeling Usage**: Value Objects are used to model concepts that are easily defined by their attributes, such as | ||
money, | ||
addresses, coordinates, etc. | ||
|
||
## Domain Model | ||
|
||
**Domain Model** is an enterprise design pattern that describes the object model of the problem domain. | ||
The domain model is a representation of meaningful real-world concepts pertinent to the domain that need to be modeled | ||
in software. | ||
|
||
* **Anemic** Domain Model is an antipattern where the domain model contains only data and no behavior. The behavior is | ||
implemented in services. This is an antipattern because it violates the object-oriented principle of encapsulation. | ||
|
||
* **Rich** Domain Model is an enterprise design pattern where the domain model contains both data and behavior. The | ||
behavior | ||
is implemented in the domain model itself. This is a good practice because it adheres to the object-oriented principle | ||
of encapsulation. | ||
|
||
## Aggregate | ||
|
||
**Aggregate** is an enterprise design pattern that represents the group of dependant domain models. | ||
The main goal is to organize objects, ensure data integrity, and manage complex relationships so that | ||
they remain consistent. | ||
|
||
### Properties | ||
|
||
* **Root Entity**: An aggregate has a root entity, which is the access point to all other objects in the aggregate. | ||
* **Coupled entities**: All objects within an aggregate are tightly coupled and should be considered a single logical | ||
unit. | ||
* **Data Integrity**: Changes within an aggregate must be made through the root entity to maintain data integrity. | ||
|
||
Domain model can be considered as an aggregate if it satisfies the conditions above, e.g. User. | ||
|
||
## Data Mapper | ||
|
||
**Data Mapper** (Persistence/ORM model) is an enterprise design pattern that maps objects to database tables. | ||
|
||
### Can Data Mapper be used as Domain Model? | ||
|
||
In DDD you have the domain model and the repository. That's it! If inside the repository you will persist the domain | ||
model directly OR if you will convert it to a persistence model before persisting it, it's up to you! It's a matter of | ||
design, your design. The domain doesn't care about how models are saved. It's an implementation detail of the | ||
repository, and it doesn't matter for the domain. That's the entire purpose of Repositories: encapsulate persistence | ||
logic & details inside it. | ||
|
||
But as developers we know it's not always possible to build a domain 100% immune from persistence interference, even | ||
they being different things. | ||
|
||
### Why this can be a good idea? | ||
|
||
* **Avoiding duplicated code**: you can use the same model for the domain and the persistence layer. | ||
* **Performance**: due to shorten the number of mapping operations, the persistence model can be more efficient. | ||
* **Take everything from ORM**: relationships, lazy loading, change tracking, and cascading are become easier to | ||
implement. | ||
|
||
### Why this can be a problem? | ||
|
||
* **ORM dependency**: our code becomes dependent on the ORM. If we decide to change the ORM, we will have to change the | ||
domain model. (_How often do you change the ORM?_) | ||
* **SRP violation**: the domain model is designed to be a representation of the business rules | ||
and logic. The persistence model is designed to be a representation of the database schema. (_Is your domain model | ||
just a mirror of the database schema? There are always victims, whether it SRP or DRY violation_) | ||
* **Consistency**: we usually work with aggregates to ensure consistency and invariants. The persistence model is just | ||
a representation of one table, and it doesn't have the same constraints as the domain model. (_You can use the | ||
relationships, custom properties, and methods to ensure consistency, so it's not a big deal_) | ||
|
||
### What is the best approach? | ||
|
||
It depends on your project. And not the size of the project, but the complexity of the domain. If you have a | ||
really complex business rules, it's better to separate the domain model from the persistence model. But it is not a | ||
necessity or a silver bullet that will solve all your problems. I guess the best approach is to start with a single | ||
model and refactor it when you feel the need. It's better to have a working code than a perfect code that doesn't | ||
work. | ||
|
||
## Active Record | ||
|
||
**Active Record** is an enterprise design pattern that allows you to store and retrieve data in a database using | ||
objects. | ||
|
||
## Data Access Object | ||
|
||
**DAO** (Data Access Object) is an enterprise pattern that is used as abstraction of data persistence. | ||
|
||
## Repository | ||
|
||
**Repository** is an enterprise pattern that mediates between the domain and persistence layers using a collection-like | ||
interface for accessing domain objects. | ||
|
||
### Collection-oriented vs Persistence-oriented | ||
|
||
* **Collection-oriented**: can be thought as an in-memory set of entities which can track object changes. | ||
Usually, you deal with this kind of repository when you are working with SQL databases and an ORM. | ||
|
||
* **Persistence-oriented**: as a list or table of entities where you cannot track their changes. | ||
Persistence-oriented repositories are more common in document (NoSQL) databases and an ODM. | ||
|
||
### Use Specific repositories instead of Generic | ||
|
||
E.g. UserRepository vs Repository[User] | ||
|
||
**Generic repository** is an a priori anti-pattern, since the repository encapsulates the logic of storing aggregates, | ||
not a specific domain model. However, it is allowed to move the general functionality to the base class. | ||
|
||
### Manage transactions outside the repository | ||
|
||
The transaction management can not be the responsibility of the repository. It is the responsibility of the client | ||
code to manage transactions. Therefore, repositories controlling transaction are considered an anti-pattern. | ||
|
||
### Use Aggregate-centred repository instead of Model-centred | ||
|
||
E.g. OrderRepository vs OrderRepository+OrderItemRepository | ||
|
||
* **Aggregate-centred** repository is the only correct approach. Order should be able to save the whole aggregate | ||
at once. This way, you can ensure that the aggregate is always consistent. | ||
|
||
* **Model-centred** repository is considered an anti-pattern. The trouble with this approach is that it doesn't take | ||
into account the consistency boundaries. You can't save Order and OrderItem separately, because they are part of the | ||
same aggregate. Otherwise, you might end up with inconsistent data. | ||
|
||
If you have a simple business logic, you might not need to use the repository pattern at all since there | ||
is DAO pattern that can be used to access the database directly. | ||
|
||
### Repository vs ORM Session | ||
|
||
**ORM Session** is more about data access, while **Repository** is more about business logic. | ||
Repository may include pagination, filtering, caching, and other business logic, while ORM Session is more about | ||
CRUD operations. | ||
|
||
### Repository vs Data Access Object | ||
|
||
The **Repository** pattern originates from **Domain-Driven Design** (DDD), as described by Eric Evans in his book, " | ||
Domain-Driven Design: Tackling Complexity in the Heart of Software." (2003) Repositories are not just about managing | ||
data; they | ||
encapsulate business logic, ensuring that operations adhere to the Ubiquitous Language of the domain. | ||
|
||
On the other hand, the **Data Access Object** (DAO) pattern comes from **early enterprise application design**, and its | ||
formalization can be found in the book "Core J2EE Patterns: Best Practices and Design Strategies" by Deepak Alur, John | ||
Crupi, and Dan Malks, published in 2001. DAO abstracts and encapsulates all access to a data source, separating | ||
persistence logic from business logic. This allows changes to the underlying data source without affecting the rest of | ||
the application, which is especially useful in enterprise contexts where systems might switch databases or data storage | ||
mechanisms over time. | ||
|
||
The primary distinction between Repository and DAO lies in the semantics of the API they expose: | ||
|
||
* **DAO**: DAO is data-centric, providing database operations such as insert, update, delete, and find. These methods | ||
directly map to database actions, focusing on how data is stored and retrieved. | ||
* **Repository**: This is domain-driven and aligns with the business logic. It represents a collection of aggregate | ||
roots | ||
or entities. Instead of database operations, repositories offer operations that reflect the domain language. For | ||
instance, in a hotel system, a Repository might provide checkIn, checkout, or checkReservation, abstracting away the | ||
actual data operations. | ||
|
||
Repository could be implemented using DAO's, but you wouldn't do the opposite. | ||
|
||
## Specification | ||
|
||
**Specification** is an enterprise design pattern that use to describe recombinable business logic in a boolean fashion. | ||
Moreover, this pattern that allows you to combine rules to create more complex rules, which you can use to filter | ||
objects | ||
|
||
### Use cases | ||
|
||
* **Finding data in the database**. This is finding records that match certain criteria. | ||
* **Checking objects in memory**. In other words, checking that the object we retrieved from the DB matches the | ||
specification. | ||
* **Creating a new instance that matches the criteria**. This is useful in cases where you don't care about the actual | ||
contents of the instances, but still need to have certain attributes. | ||
|
||
### Identity Map | ||
|
||
**Identity Map** is an enterprise design pattern used to manage the identity of objects in a data-driven application. | ||
Its main purpose is to ensure that each object loaded from the database has only one unique copy in the application. | ||
|
||
### Use cases | ||
|
||
* **Object caching**: When an object is requested, Identity Map first checks if it already exists in memory (in the | ||
cache) | ||
for the current session or transaction. If the object is already loaded, then that copy is returned instead of | ||
creating a new instance. | ||
|
||
* **Maintaining data integrity**: This prevents the problem of the same object being loaded multiple times and each | ||
instance having its own state. Instead, all changes to the object will be reflected in a single instance. | ||
|
||
* **Speeding up data**: Since Identity Map stores already loaded objects in the cache, it reduces the need for | ||
repeated database hits, improving performance. | ||
|
||
## Unit of Work | ||
|
||
**Unit of Work** is an enterprise design pattern that maintains a list of objects affected by a business transaction. | ||
It coordinates the writing out of changes and the resolution of concurrency problems. | ||
|
||
## References | ||
|
||
* [Domain Model - Martin Fowler](https://martinfowler.com/eaaCatalog/domainModel.html) | ||
* [Aggregate - Martin Fowler](https://martinfowler.com/bliki/DDD_Aggregate.html) | ||
* [Data Mapper - Martin Fowler](https://martinfowler.com/eaaCatalog/dataMapper.html) | ||
* [Active Record - Martin Fowler](https://martinfowler.com/eaaCatalog/activeRecord.html) | ||
* [Repository - Martin Fowler](https://martinfowler.com/eaaCatalog/repository.html) | ||
* [Specification - Martin Fowler](https://martinfowler.com/apsupp/spec.pdf) | ||
* [Identity Map - Martin Fowler](https://martinfowler.com/eaaCatalog/identityMap.html) | ||
* [Unit of Work - Martin Fowler](https://martinfowler.com/eaaCatalog/unitOfWork.html) | ||
* [Differnces between repository and DAO - DZone](https://dzone.com/articles/differences-between-repository-and-dao) | ||
* [Repository DDD in Ruby on rails - Visuality](https://www.visuality.pl/posts/repository-ddd-in-ruby-on-rails) | ||
* [What is a Repository - ShawnMc.Cool](https://shawnmc.cool/what-is-a-repository) | ||
* [Паттерн проектирования Спецификация - Bool](https://jopr-site.azureedge.net/blog/detail/spetsifikatsiya-pattern-proektirovaniya) | ||
* [Implementing a Unit of Work - SitePoint](https://www.sitepoint.com/implementing-a-unit-of-work/) | ||
* [The Unit of Work Design Pattern Explained](https://www.youtube.com/watch?v=HX6vkP-QD7U) | ||
* [Unit of Work: от простого к сложному](https://www.youtube.com/watch?v=oP_OUiIK4Rc) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.