refactor: clean project based session03 notes
This commit is contained in:
+19
-30
@@ -1,11 +1,10 @@
|
|||||||
# Introduction to Repository Pattern (Chat GPT):
|
## Introduction to Repository Pattern (Chat GPT):
|
||||||
The **Repository Pattern** in ASP.NET Core is a design pattern used to separate business logic from data access logic by providing an abstraction layer over database operations. This pattern improves maintainability, testability, and flexibility in applications by encapsulating database operations in dedicated repository classes.
|
The **Repository Pattern** in ASP.NET Core is a design pattern used to separate business logic from data access logic by providing an abstraction layer over database operations. This pattern improves maintainability, testability, and flexibility in applications by encapsulating database operations in dedicated repository classes.
|
||||||
|
|
||||||
---
|
---
|
||||||
## **Why Use the Repository Pattern?**
|
### **Why Use the Repository Pattern?**
|
||||||
|
|
||||||
### **Pros:**
|
|
||||||
|
|
||||||
|
#### **Pros:**
|
||||||
1. **Abstraction from ORM (Entity Framework Core)**
|
1. **Abstraction from ORM (Entity Framework Core)**
|
||||||
- Prevents direct dependency on EF Core, making it easier to swap out the data access layer in the future.
|
- Prevents direct dependency on EF Core, making it easier to swap out the data access layer in the future.
|
||||||
|
|
||||||
@@ -24,7 +23,7 @@ The **Repository Pattern** in ASP.NET Core is a design pattern used to separate
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## **Comparison: Repository Pattern vs. Direct DbSet Operations**
|
### **Comparison: Repository Pattern vs. Direct DbSet Operations**
|
||||||
|
|
||||||
| Feature | Using Repository Pattern | Using DbSet Directly in Controllers |
|
| Feature | Using Repository Pattern | Using DbSet Directly in Controllers |
|
||||||
| -------------------------- | -------------------------------------- | -------------------------------------- |
|
| -------------------------- | -------------------------------------- | -------------------------------------- |
|
||||||
@@ -35,50 +34,41 @@ The **Repository Pattern** in ASP.NET Core is a design pattern used to separate
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## **Implementation of the Repository Pattern**
|
## Introduction to Unit of Work Pattern (Chat GPT):
|
||||||
|
|
||||||
We will create:
|
### **What is the Unit of Work Pattern?**
|
||||||
|
|
||||||
1. **IRepository** (Generic repository interface)
|
The **Unit of Work (`UoW`)** pattern is a **centralized mechanism** to manage **database transactions** and ensure that multiple repository operations are treated as a single unit of execution. It acts as a wrapper around multiple repositories to **coordinate their changes and commit them in one go**.
|
||||||
2. **Repository** (Generic repository implementation)
|
|
||||||
3. **Entity-specific repositories** (e.g., `ICustomerRepository` and `CustomerRepository`)
|
|
||||||
|
|
||||||
|
|
||||||
# Introduction to Unit of Work Pattern (Chat GPT):
|
|
||||||
|
|
||||||
## **What is the Unit of Work Pattern?**
|
|
||||||
|
|
||||||
The **Unit of Work (UoW)** pattern is a **centralized mechanism** to manage **database transactions** and ensure that multiple repository operations are treated as a single unit of execution. It acts as a wrapper around multiple repositories to **coordinate their changes and commit them in one go**.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## **Advantages of Unit of Work**
|
### **Advantages of Unit of Work**
|
||||||
|
|
||||||
### **1. Single Transaction for Multiple Operations**
|
#### **1. Single Transaction for Multiple Operations**
|
||||||
|
|
||||||
- If you're performing **multiple database operations** across different repositories, **Unit of Work ensures atomicity**.
|
- If you're performing **multiple database operations** across different repositories, **Unit of Work ensures atomicity**.
|
||||||
- If one operation fails, everything is **rolled back** (when using explicit transactions).
|
- If one operation fails, everything is **rolled back** (when using explicit transactions).
|
||||||
|
|
||||||
### **2. Better Performance**
|
#### **2. Better Performance**
|
||||||
|
|
||||||
- **Without UoW:** Every repository would call `SaveChangesAsync()` separately, causing multiple round trips to the database.
|
- **Without UoW:** Every repository would call `SaveChangesAsync()` separately, causing multiple round trips to the database.
|
||||||
- **With UoW:** All changes are saved **at once**, reducing the number of database calls.
|
- **With UoW:** All changes are saved **at once**, reducing the number of database calls.
|
||||||
### **3. Maintains Consistency**
|
#### **3. Maintains Consistency**
|
||||||
- When multiple repositories modify related entities, **UoW ensures that all changes are either committed or discarded together**.
|
- When multiple repositories modify related entities, **UoW ensures that all changes are either committed or discarded together**.
|
||||||
### **4. Improves Testability**
|
#### **4. Improves Testability**
|
||||||
- Unit of Work allows you to **mock database changes** and write unit tests efficiently without worrying about inconsistent data states.
|
- Unit of Work allows you to **mock database changes** and write unit tests efficiently without worrying about inconsistent data states.
|
||||||
### **5. Prevents Partial Updates**
|
#### **5. Prevents Partial Updates**
|
||||||
- If multiple repositories handle different entities in the same operation, calling `SaveChangesAsync()` in individual repositories could lead to **partial updates** if one operation succeeds and another fails.
|
- If multiple repositories handle different entities in the same operation, calling `SaveChangesAsync()` in individual repositories could lead to **partial updates** if one operation succeeds and another fails.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## **Why Should `SaveChanges()` NOT Be in the Repository?**
|
### **Why Should `SaveChanges()` NOT Be in the Repository?**
|
||||||
|
|
||||||
### **1. Each Repository Should Not Control Transactions**
|
#### **1. Each Repository Should Not Control Transactions**
|
||||||
|
|
||||||
If each repository calls `SaveChangesAsync()`, **you lose control over transactions**.
|
If each repository calls `SaveChangesAsync()`, **you lose control over transactions**.
|
||||||
|
|
||||||
#### **Example Problem (Without UoW)**
|
##### **Example Problem (Without `UoW`)**
|
||||||
|
|
||||||
Imagine you have two repositories: `CustomerRepository` and `OrderRepository`.
|
Imagine you have two repositories: `CustomerRepository` and `OrderRepository`.
|
||||||
If you try to **add a customer** and **add an order** separately, each calling `SaveChangesAsync()`:
|
If you try to **add a customer** and **add an order** separately, each calling `SaveChangesAsync()`:
|
||||||
@@ -98,7 +88,7 @@ await _orderRepository.SaveChangesAsync(); // ❌ Second database call
|
|||||||
- The customer has already been saved, but the order is missing.
|
- The customer has already been saved, but the order is missing.
|
||||||
- **Your database is left in an inconsistent state!**
|
- **Your database is left in an inconsistent state!**
|
||||||
|
|
||||||
### **2. Database Round Trips (Performance Issue)**
|
#### **2. Database Round Trips (Performance Issue)**
|
||||||
|
|
||||||
If each repository calls `SaveChangesAsync()`, you end up with **multiple database calls** instead of batching them into a single transaction.
|
If each repository calls `SaveChangesAsync()`, you end up with **multiple database calls** instead of batching them into a single transaction.
|
||||||
|
|
||||||
@@ -115,17 +105,16 @@ await _unitOfWork.SaveChangesAsync(); // ✅ One database call
|
|||||||
|
|
||||||
This **reduces network latency** and improves **database performance**.
|
This **reduces network latency** and improves **database performance**.
|
||||||
|
|
||||||
### **3. Promotes Separation of Concerns**
|
#### **3. Promotes Separation of Concerns**
|
||||||
|
|
||||||
- **Repositories should focus on CRUD operations** (data retrieval and manipulation).
|
- **Repositories should focus on CRUD operations** (data retrieval and manipulation).
|
||||||
- **Unit of Work should manage transactions**.
|
- **Unit of Work should manage transactions**.
|
||||||
- This makes the code **cleaner and easier to maintain**.
|
- This makes the code **cleaner and easier to maintain**.
|
||||||
---
|
---
|
||||||
|
|
||||||
## **Key Takeaways**
|
### **Key Takeaways**
|
||||||
|
|
||||||
✔ **Unit of Work ensures all database operations are part of a single transaction**.
|
✔ **Unit of Work ensures all database operations are part of a single transaction**.
|
||||||
✔ **Repositories should NOT call `SaveChangesAsync()` to avoid multiple transactions**.
|
✔ **Repositories should NOT call `SaveChangesAsync()` to avoid multiple transactions**.
|
||||||
✔ **EF Core tracks changes, so calling `SaveChangesAsync()` once is enough**.
|
✔ **EF Core tracks changes, so calling `SaveChangesAsync()` once is enough**.
|
||||||
✔ **Using UoW improves performance, consistency, and maintainability**.
|
✔ **Using UoW improves performance, consistency, and maintainability**.
|
||||||
--
|
|
||||||
@@ -1,13 +1,26 @@
|
|||||||
|
|
||||||
# 1. Implementing Repository Pattern
|
# 🛠️ Task Checklist
|
||||||
## Branching
|
|
||||||
|
## Repository Pattern Preparation
|
||||||
|
- [ ] Watch [video 1 (Brief Introduction](https://www.youtube.com/watch?v=Wiy54682d1w&ab_channel=PatrickGod) & [video 2 (More Detailed Introduction)](https://youtu.be/rtXpYpZdOzM)
|
||||||
|
### Purpose:
|
||||||
|
Meditates between the domain and data mapping layers, acting like an **in-memory collection** of domain objects
|
||||||
|
### Benefits
|
||||||
|
- Minimizes duplicate query logic
|
||||||
|
- Decouples your application from persistence frameworks
|
||||||
|
- Promotes testability
|
||||||
|
> Repository should not have methods like Update and Save
|
||||||
|
## Unit of Work Preparation
|
||||||
|
Keeps track of changes and coordinates the writings and savings
|
||||||
|
### Implementation:
|
||||||
|
- [ ] Watch [video](https://youtu.be/rtXpYpZdOzM?t=703)
|
||||||
|
|
||||||
|
## 🚧Branching (Implementing Repository Pattern)
|
||||||
- [ ] Create the feature/repositories branch based on develop
|
- [ ] Create the feature/repositories branch based on develop
|
||||||
|
|
||||||
## Create `IRepository` in Domain
|
## Creating `IRepository` in Domain
|
||||||
- [ ] Create the interface and add the following code
|
- [ ] Create the interface and add the following code
|
||||||
|
|
||||||
📂 Suggested Folder: `Domain/Framework/Interfaces/Respositories`
|
📂 Suggested Folder: `Domain/Framework/Interfaces/Respositories`
|
||||||
|
|
||||||
```c#
|
```c#
|
||||||
public interface IRepository<T_Entity, U_PrimaryKey> where T_Entity : class
|
public interface IRepository<T_Entity, U_PrimaryKey> where T_Entity : class
|
||||||
{
|
{
|
||||||
@@ -19,14 +32,11 @@ public interface IRepository<T_Entity, U_PrimaryKey> where T_Entity : class
|
|||||||
void Remove(T_Entity entity);
|
void Remove(T_Entity entity);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
## Creating a class implementing `IRepository`
|
||||||
## Create a class implementing `IRepository`
|
|
||||||
- [ ] create a class named `BaseRepository` or `Repository` (choose one) in Infrastructure and implement `IRepository`
|
- [ ] create a class named `BaseRepository` or `Repository` (choose one) in Infrastructure and implement `IRepository`
|
||||||
|
|
||||||
📂 Suggested Folder: `Infrastructure/Framework/Base`
|
📂 Suggested Folder: `Infrastructure/Framework/Base`
|
||||||
|
|
||||||
- [ ] provide method definitions for the methods
|
- [ ] provide method definitions for the methods
|
||||||
|
|
||||||
```c#
|
```c#
|
||||||
public class BaseRepository<K_DbContext, T_Entity, U_PrimaryKey> : IRepository<T_Entity, U_PrimaryKey>
|
public class BaseRepository<K_DbContext, T_Entity, U_PrimaryKey> : IRepository<T_Entity, U_PrimaryKey>
|
||||||
where T_Entity : class
|
where T_Entity : class
|
||||||
@@ -71,11 +81,13 @@ public class BaseRepository<K_DbContext, T_Entity, U_PrimaryKey> : IRepository<T
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Create one interface for each entity(except the join tables for now), and name it `I[Entity]Repository`
|
## Creating an interface for each entity
|
||||||
|
> (not for the join tables)
|
||||||
|
|
||||||
- [ ] For each entity, create an interface that inherits `IRepository`
|
- [ ] For each entity, create an interface that inherits `IRepository`
|
||||||
- [ ] (Optional): Add method definitions as you deem needed for that entity (not recommended right now. We will come back to this part later)
|
- [ ] (Optional): Add method definitions as you deem needed for that entity (not recommended right now. We will come back to this part later)
|
||||||
|
|
||||||
📂 Suggested Folder: Domain/Framework/Base/Interfaces/{Related Folder}`
|
📂 Suggested Folder: Domain/Framework/Base/Interfaces/[Related Folder]`
|
||||||
for example:
|
for example:
|
||||||
```c#
|
```c#
|
||||||
public interface IAccountRepository : IRepository<Account, long>
|
public interface IAccountRepository : IRepository<Account, long>
|
||||||
@@ -85,11 +97,10 @@ public interface IAccountRepository : IRepository<Account, long>
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Reference Project:
|
### Reference Project:
|
||||||
https://github.com/MehrdadShirvani/AlibabaClone-Backend/tree/develop/AlibabaClone.Domain/Framework/Interfaces/Repositories
|
[Reference](https://github.com/MehrdadShirvani/AlibabaClone-Backend/tree/develop/AlibabaClone.Domain/Framework/Interfaces/Repositories)
|
||||||
## Create one class for each entity, implementing the `I[Entity]Repository`
|
## Implementing each `I[Entity]Repository`
|
||||||
|
|
||||||
📂 Suggested Folder: Infrastructure/Services/{Related Folder}
|
|
||||||
|
|
||||||
|
📂 Suggested Folder: Infrastructure/Services/[Related Folder]
|
||||||
- [ ] For each entity, create an class named `[Entity]Repository` that implements `I[Entity]Repository` and inherits `BaseRepository`
|
- [ ] For each entity, create an class named `[Entity]Repository` that implements `I[Entity]Repository` and inherits `BaseRepository`
|
||||||
|
|
||||||
for example:
|
for example:
|
||||||
@@ -105,7 +116,8 @@ public class AccountRepository :
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
### Reference Project:
|
### Reference Project:
|
||||||
https://github.com/MehrdadShirvani/AlibabaClone-Backend/tree/develop/AlibabaClone.Infrastructure/Services
|
[Reference](https://github.com/MehrdadShirvani/AlibabaClone-Backend/tree/develop/AlibabaClone.Infrastructure/Services)
|
||||||
|
|
||||||
## Registering Services
|
## Registering Services
|
||||||
- [ ] Modify `Program.cs` in Presentation, and register for each `I[Entity]Repository` the related `[Entity]Repository`
|
- [ ] Modify `Program.cs` in Presentation, and register for each `I[Entity]Repository` the related `[Entity]Repository`
|
||||||
|
|
||||||
@@ -116,31 +128,17 @@ builder.Services.AddScoped<IAccountRepository, AccountRepository>();
|
|||||||
builder.Services.AddScoped<IGenderRepository, GenderRepository>();
|
builder.Services.AddScoped<IGenderRepository, GenderRepository>();
|
||||||
builder.Services.AddScoped<IPersonRepository, PersonRepository>();
|
builder.Services.AddScoped<IPersonRepository, PersonRepository>();
|
||||||
builder.Services.AddScoped<IRoleRepository, RoleRepository>();
|
builder.Services.AddScoped<IRoleRepository, RoleRepository>();
|
||||||
|
//...
|
||||||
|
|
||||||
builder.Services.AddScoped<ICompanyRepository, CompanyRepository>();
|
|
||||||
|
|
||||||
builder.Services.AddScoped<ICityRepository, CityRepository>();
|
|
||||||
builder.Services.AddScoped<ILocationRepository, LocationRepository>();
|
|
||||||
builder.Services.AddScoped<ILocationTypeRepository, LocationTypeRepository>();
|
|
||||||
|
|
||||||
builder.Services.AddScoped<ITransactionRepository, TransactionRepository>();
|
|
||||||
|
|
||||||
builder.Services.AddScoped<ITicketRepository, TicketRepository>();
|
|
||||||
builder.Services.AddScoped<ITicketStatusRepository, TicketStatusRepository>();
|
|
||||||
builder.Services.AddScoped<ITransportationRepository, TransportationRepository>();
|
|
||||||
|
|
||||||
builder.Services.AddScoped<ISeatRepository, SeatRepository>();
|
|
||||||
builder.Services.AddScoped<IVehicleRepository, VehicleRepository>();
|
|
||||||
builder.Services.AddScoped<IVehicleTypeRepository, VehicleTypeRepository>();
|
|
||||||
//some code
|
//some code
|
||||||
```
|
```
|
||||||
## Merge
|
## 🚧Merge
|
||||||
- [ ] Create a PR and merge the current branch with develop
|
- [ ] Create a PR and merge the current branch with develop
|
||||||
|
|
||||||
|
|
||||||
# 2. Implementing Unit of Work Pattern
|
---
|
||||||
|
|
||||||
## Branching
|
## 🚧Branching
|
||||||
- [ ] Create the feature/UnitOfWork branch based on develop
|
- [ ] Create the feature/UnitOfWork branch based on develop
|
||||||
|
|
||||||
|
|
||||||
@@ -151,7 +149,7 @@ public interface IUnitOfWork : IDisposable
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Create `IUnitOfWork` in Domain
|
## Creating `IUnitOfWork`
|
||||||
- [ ] Create the interface that inherits `IDisposable` and add the following code
|
- [ ] Create the interface that inherits `IDisposable` and add the following code
|
||||||
|
|
||||||
📂 Suggested Folder: `Domain/Framework/Interfaces`
|
📂 Suggested Folder: `Domain/Framework/Interfaces`
|
||||||
@@ -163,7 +161,7 @@ public interface IUnitOfWork : IDisposable
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Create a class implementing `IUnitOfWork`
|
## Implementing `IUnitOfWork`
|
||||||
- [ ] create a class named `UnitOfWork` in Infrastructure and implement `IUnitOfWork`
|
- [ ] create a class named `UnitOfWork` in Infrastructure and implement `IUnitOfWork`
|
||||||
|
|
||||||
📂 Suggested Folder: `Infrastructure/Framework/Base`
|
📂 Suggested Folder: `Infrastructure/Framework/Base`
|
||||||
@@ -202,5 +200,15 @@ builder.Services.AddScoped<IUnitOfWork, UnitOfWork>();
|
|||||||
//some code
|
//some code
|
||||||
```
|
```
|
||||||
|
|
||||||
## Merge
|
## 🚧Merge
|
||||||
- [ ] Create a PR and merge the current branch with develop
|
- [ ] Create a PR and merge the current branch with develop
|
||||||
|
|
||||||
|
|
||||||
|
# 🧠 Hints & Notes
|
||||||
|
# 🙌 Acknowledgements
|
||||||
|
|
||||||
|
- ChatGPT for snippet refinement and explanations
|
||||||
|
# 🔍 References
|
||||||
|
[[Session03 Additional Info]]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user