(From the Pluralsight course)
Presentation
Layer- showing data to and accepting input from end users
Service
Layer- BFF (Backend for Frontend) or API Gateway.
- Single point of entry for all outside traffic.
- Present stable API to Presentation Layer, and allow μs to change independent of frontend.
- Microservices
- Implement Domain Logic here.
- Implement Data Access here.
- Separate domain logic and data access.
(From the Pluralsight course)
💡 Apply the domain logic and data-access patterns which make most sense to each individual microservice.
Example of eShopOnContainers
e-commerce application that has Catalog
, Order
and Basket
μs. Now we want to roll out some promotions ("add two T-Shirts to basket, get a cup free").
- Where should the "promotions" data be contained?
- Where should the "promotions" logic be contained?
One option:
- Store promotions data in the
Catalog
μs. - Store promotions logic in the
Basket
orOrder
μs.
If the promotions become more complex, consider rolling out a new μs:
- Avoid building domain logic into the API Gateway (BFF) or the Frontend Applications.
- avoid duplicating domain logic across μs
- do not give too many responsibilities to one μs
- avoid chatty communications across μs in the context of one user operation.
- prefer async communication between μs.
(From the Pluralsight course)
- A single procedure for each request from presentation layer
- Mixes Domain Logic and Data Access in same function (perhaps with a thin wrapper around Database)
Pros ✅
- Simple as long as domain logic is simple
- easy to see big picture, reason about, easy to adopt
Cons ❌
- as domain logic increases, methods get complex
- because Data Access and Domain Logic are mixed together, methods cannot be effectively unit-tested
- Duplication is possible if different frontend applications need to reuse business logic
- Exposes public methods on our objects that represent business operations.
- Uses the language of the business.
- Does not contain any data access code.
- E.g. an Order can only go through specific statuses and it can only transition between statuses in specific sequence.
Examples of carefully controlling access to the state of the order
The only way these models can be modified is by intentionally exposing only one public method that adds items to order.
💡 Domain Model pattern does not have any references to data access in objects. Data access is done through the Data Mapping Layer.
- Retrieve data from database
- Convert it into objects used by the domain logic.
- These objects could be
- Domain Models OR
- Data Transfer Objects
Pros
- Model never goes into an invalid state or goes through an invalid transition between states
Approaches to Data Mapping
Object Relational Mapper (ORM) features:
- Auto-generate SQL statements
- Navigate relationships
- Track modifications
- Simplify data access.
Example, if a Orders
table ha a 1:many relationship to an OrderItems
table, an ORM can auto-load the order items when we fetch an order.
Micro ORMs
- SQL auto-generated by ORMs can be inefficient.
- Micro ORMs help mitigate this with more control over SQL and dynamic typing.
Document Databases
- Allow you to represent an order as the order data PLUS order items.
- Rarely need "joins"
- Simplifies reading and updating individual items
Serverless Frameworks
- Allows data access via "bindings"
- Less boilerplate code to connect to DB
- However, advanced data access scenarios have limited support
Unit of Work pattern
- Batch up all changes into a single atomic operation and update changes into DB
- Prevents DB from going into invalid state.
- All the API endpoints either represent a command (write) or query (read)
- Commands
- Should only update data and not return any data
- Queries
- Should only return data and not update any data
💡 CQRS advocates the use of separate model objects for commands and queries and Make use of separate code paths for commands and queries.
- i.e do not use the same objects to represent data access and Data Transfer Objects (DTOs)
- This allows the database schema to change independently from public API of your microservice.
💡 Domain Model pattern used one class per entity, e.g. one Order
object per row in Orders
DB. Table module uses one class per table, i.e. one Orders
class containing all the domain logic for the Orders
table.
- Useful for batch operations.
- May be used as in intermediate step when refactoring from Transaction Script pattern to Domain Model pattern.