refactor: clean project based sesstion04 notes
This commit is contained in:
@@ -1,34 +1,24 @@
|
|||||||
### 1. **Should I have an `IEntityService` and then `EntityService` for each of my entities?**
|
## *Should I have an `IEntityService` and then `EntityService` for each of my entities?**
|
||||||
|
|
||||||
Not necessarily **for _every_** entity — only if it **makes sense**.
|
Not necessarily **for _every_** entity — only if it **makes sense**.
|
||||||
|
|
||||||
- The Application Layer should expose **use cases** — not just CRUD logic for each entity.
|
- The Application Layer should expose **use cases** — not just CRUD logic for each entity.
|
||||||
|
|
||||||
- If an entity has business logic or interactions that need orchestration (e.g., validations, aggregations, calling repositories, etc.), then **yes**, create a service.
|
- If an entity has business logic or interactions that need orchestration (e.g., validations, aggregations, calling repositories, etc.), then **yes**, create a service.
|
||||||
|
|
||||||
- Otherwise, for basic operations, **directly using a repository (via a unit of work or interface)** from the use case handler might be fine.
|
- Otherwise, for basic operations, **directly using a repository (via a unit of work or interface)** from the use case handler might be fine.
|
||||||
### 2. **Is it OK to have services not related to a specific entity?**
|
## **Is it OK to have services not related to a specific entity?**
|
||||||
|
|
||||||
Absolutely, **yes**. In fact, that’s expected in a Clean Architecture setup.
|
Absolutely, **yes**. In fact, that’s expected in a Clean Architecture setup.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
- A `ReportGenerationService` that combines bookings, customers, and payments.
|
- A `ReportGenerationService` that combines bookings, customers, and payments.
|
||||||
|
|
||||||
- A `TokenService` for authentication tokens.
|
- A `TokenService` for authentication tokens.
|
||||||
|
|
||||||
- A `CurrencyConversionService` that hits an external API.
|
- A `CurrencyConversionService` that hits an external API.
|
||||||
|
|
||||||
- A `NotificationService` that sends emails or SMS.
|
- A `NotificationService` that sends emails or SMS.
|
||||||
|
|
||||||
|
|
||||||
👉 As long as these services **live in the Application Layer** and follow **dependency inversion** (i.e., they depend only on interfaces, not implementations), you’re doing great.
|
👉 As long as these services **live in the Application Layer** and follow **dependency inversion** (i.e., they depend only on interfaces, not implementations), you’re doing great.
|
||||||
|
|
||||||
### 3. **Is it necessary to have an interface for each service?**
|
## **Is it necessary to have an interface for each service?**
|
||||||
|
|
||||||
|
## **What’s the Difference Between Services and Repositories?**
|
||||||
|
|
||||||
### 🔹 **What’s the Difference Between Services and Repositories?**
|
|
||||||
|
|
||||||
| Aspect | **Service** | **Repository** |
|
| Aspect | **Service** | **Repository** |
|
||||||
| ------------------ | ---------------------------------------------------------------------- | --------------------------------------------------------------- |
|
| ------------------ | ---------------------------------------------------------------------- | --------------------------------------------------------------- |
|
||||||
@@ -37,7 +27,7 @@ Examples:
|
|||||||
| **Focus** | Coordinates multiple domain/repo operations, validation, business flow | Fetching/storing data for a specific entity |
|
| **Focus** | Coordinates multiple domain/repo operations, validation, business flow | Fetching/storing data for a specific entity |
|
||||||
| **Example** | `PlaceOrderService`, `ReportService` | `ICustomerRepository`, `IOrderRepository` |
|
| **Example** | `PlaceOrderService`, `ReportService` | `ICustomerRepository`, `IOrderRepository` |
|
||||||
|
|
||||||
### What does `init` mean?
|
## What does `init` mean?
|
||||||
|
|
||||||
`init` is an **access modifier for properties** that allows you to **set a property only during object initialization**, **but not after**.
|
`init` is an **access modifier for properties** that allows you to **set a property only during object initialization**, **but not after**.
|
||||||
|
|
||||||
@@ -62,21 +52,15 @@ Examples:
|
|||||||
### But when should you prefer `class`?
|
### But when should you prefer `class`?
|
||||||
|
|
||||||
Use `class` if your DTO or model:
|
Use `class` if your DTO or model:
|
||||||
|
|
||||||
- Needs to be **mutable** after creation
|
- Needs to be **mutable** after creation
|
||||||
|
|
||||||
- Has to **interact with legacy APIs/libraries**
|
- Has to **interact with legacy APIs/libraries**
|
||||||
|
|
||||||
- Needs **inheritance or polymorphism** (not well supported in `record`)
|
- Needs **inheritance or polymorphism** (not well supported in `record`)
|
||||||
|
|
||||||
- Has **rich behavior** (logic, methods, validation, etc.)
|
- Has **rich behavior** (logic, methods, validation, etc.)
|
||||||
|
|
||||||
|
|
||||||
> For example, in the Domain Layer (Entities, ValueObjects), you'll usually stick to **`class`** — because that's where behavior lives.
|
> For example, in the Domain Layer (Entities, ValueObjects), you'll usually stick to **`class`** — because that's where behavior lives.
|
||||||
|
|
||||||
## RESTful APIs:
|
## RESTful APIs:
|
||||||
https://aws.amazon.com/what-is/restful-api/#:~:text=RESTful%20API%20is%20an%20interface,applications%20to%20perform%20various%20tasks.
|
[read more](https://aws.amazon.com/what-is/restful-api/#:~:text=RESTful%20API%20is%20an%20interface,applications%20to%20perform%20various%20tasks.)
|
||||||
|
|
||||||
## What Conditions Make an API RESTful?
|
## What Conditions Make an API RESTful?
|
||||||
|
|
||||||
### Key Principles of REST:
|
### Key Principles of REST:
|
||||||
@@ -140,22 +124,15 @@ public class CustomerController : ControllerBase {
|
|||||||
|
|
||||||
|
|
||||||
## List-like stuff in `C#`
|
## List-like stuff in `C#`
|
||||||
Absolutely, let’s go over the main “list-like” data types in C#. They all serve similar purposes—holding multiple items—but differ in functionality, performance, and use cases. Here’s a detailed breakdown:
|
They all serve similar purposes—holding multiple items—but differ in functionality, performance, and use cases. Here’s a detailed breakdown:
|
||||||
|
|
||||||
🔷 1. `IEnumerable`
|
🔷 1. `IEnumerable`
|
||||||
|
|
||||||
- Namespace: System.Collections.Generic
|
- Namespace: System.Collections.Generic
|
||||||
|
|
||||||
- Most basic "list-like" abstraction.
|
- Most basic "list-like" abstraction.
|
||||||
|
|
||||||
- Read-only (forward-only iteration).
|
- Read-only (forward-only iteration).
|
||||||
|
|
||||||
- You can use foreach on it.
|
- You can use foreach on it.
|
||||||
|
|
||||||
- Doesn’t support indexing (no .Count, no [i]).
|
- Doesn’t support indexing (no .Count, no [i]).
|
||||||
|
|
||||||
- Often used as the return type to expose a stream of data without giving full collection control.
|
- Often used as the return type to expose a stream of data without giving full collection control.
|
||||||
|
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
@@ -166,42 +143,28 @@ foreach (var num in numbers)
|
|||||||
```
|
```
|
||||||
|
|
||||||
💡 Ideal when:
|
💡 Ideal when:
|
||||||
|
|
||||||
- You want to return a sequence without exposing modification.
|
- You want to return a sequence without exposing modification.
|
||||||
|
|
||||||
- You’re using LINQ chains.
|
- You’re using LINQ chains.
|
||||||
|
|
||||||
- You’re returning data from a database query.
|
- You’re returning data from a database query.
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
🔷 2. ICollection
|
🔷 2. ICollection
|
||||||
|
|
||||||
- Extends IEnumerable.
|
- Extends IEnumerable.
|
||||||
|
|
||||||
- Adds Count and Add/Remove/Clear methods.
|
- Adds Count and Add/Remove/Clear methods.
|
||||||
|
|
||||||
- Still abstract—List and HashSet implement it.
|
- Still abstract—List and HashSet implement it.
|
||||||
|
|
||||||
|
|
||||||
💡 Useful when:
|
💡 Useful when:
|
||||||
|
|
||||||
- You want to expose a collection that can be modified (e.g. Add or Remove).
|
- You want to expose a collection that can be modified (e.g. Add or Remove).
|
||||||
|
|
||||||
- You care about the Count.
|
- You care about the Count.
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
🔷 3. IList
|
🔷 3. `IList`
|
||||||
|
- Extends `ICollection` and `IEnumerable`.
|
||||||
- Extends ICollection and IEnumerable.
|
- Adds index access: `list[0]` etc.
|
||||||
|
|
||||||
- Adds index access: list[0] etc.
|
|
||||||
|
|
||||||
- Think of it like a mutable array with dynamic size.
|
- Think of it like a mutable array with dynamic size.
|
||||||
|
|
||||||
|
|
||||||
💡 Use when:
|
💡 Use when:
|
||||||
|
|
||||||
@@ -215,15 +178,10 @@ foreach (var num in numbers)
|
|||||||
🔷 4. List
|
🔷 4. List
|
||||||
|
|
||||||
- A concrete class (not interface).
|
- A concrete class (not interface).
|
||||||
|
- Implements `IList`, `ICollection`, `IEnumerable`.
|
||||||
- Implements IList, ICollection, IEnumerable.
|
|
||||||
|
|
||||||
- Backed by an array (auto-resizes).
|
- Backed by an array (auto-resizes).
|
||||||
|
|
||||||
- Fast read and write.
|
- Fast read and write.
|
||||||
|
- Supports `Add`, `Remove`, `Insert`, `IndexOf`, etc.
|
||||||
- Supports Add, Remove, Insert, IndexOf, etc.
|
|
||||||
|
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
@@ -238,32 +196,23 @@ var second = list[1]; // "Two"
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
🔷 5. IReadOnlyCollection & IReadOnlyList
|
🔷 5. `IReadOnlyCollection` & `IReadOnlyList`
|
||||||
|
|
||||||
- IReadOnlyCollection: Just Count and IEnumerable.
|
- `IReadOnlyCollection`: Just Count and `IEnumerable`.
|
||||||
|
- `IReadOnlyList`: Adds indexing without modification.
|
||||||
- IReadOnlyList: Adds indexing without modification.
|
|
||||||
|
|
||||||
- Used to expose lists safely (read-only).
|
- Used to expose lists safely (read-only).
|
||||||
|
|
||||||
|
|
||||||
💡 Used when:
|
💡 Used when:
|
||||||
|
|
||||||
- You want to return a list, but prevent any changes.
|
- You want to return a list, but prevent any changes.
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
🔷 6. Array (T[])
|
🔷 6. `Array(T[])`
|
||||||
|
|
||||||
- Fixed-size.
|
- Fixed-size.
|
||||||
|
|
||||||
- Fastest for indexing.
|
- Fastest for indexing.
|
||||||
|
|
||||||
- Cannot change size.
|
- Cannot change size.
|
||||||
|
- Implements `IList` (via Array).
|
||||||
- Implements IList (via Array).
|
|
||||||
|
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
@@ -276,27 +225,20 @@ numbers[0] = 42;
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
🔷 7. ObservableCollection
|
🔷 7. `ObservableCollection`
|
||||||
|
|
||||||
- For WPF/Blazor/WinForms data-binding.
|
- For WPF/`Blazor`/WinForms data-binding.
|
||||||
|
|
||||||
- Notifies UI when items are added/removed.
|
- Notifies UI when items are added/removed.
|
||||||
|
|
||||||
- Implements IList.
|
- Implements IList.
|
||||||
|
|
||||||
|
|
||||||
💡 Use in UI apps when the view needs to react to collection changes.
|
💡 Use in UI apps when the view needs to react to collection changes.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
🔷 8. HashSet
|
🔷 8. HashSet
|
||||||
|
|
||||||
- Unordered, no duplicates.
|
- Unordered, no duplicates.
|
||||||
|
- Implements `ICollection`, not `IList`.
|
||||||
- Implements ICollection, not IList.
|
|
||||||
|
|
||||||
- No index access.
|
- No index access.
|
||||||
|
|
||||||
|
|
||||||
💡 Best for fast membership checking (contains x).
|
💡 Best for fast membership checking (contains x).
|
||||||
|
|
||||||
@@ -315,6 +257,3 @@ numbers[0] = 42;
|
|||||||
|ObservableCollection|✔️|✔️|✔️|✔️|UI binding for collections|
|
|ObservableCollection|✔️|✔️|✔️|✔️|UI binding for collections|
|
||||||
|HashSet|❌|✔️|❌|❌|Fast lookup, uniqueness enforcement|
|
|HashSet|❌|✔️|❌|❌|Fast lookup, uniqueness enforcement|
|
||||||
|
|
||||||
—
|
|
||||||
|
|
||||||
If you tell me the scenario (e.g. search results, modifying a cart, populating a drop-down), I can recommend the best type for it. Want that?
|
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
# Preparation:
|
|
||||||
|
|
||||||
|
# 🛠️ Task Checklist
|
||||||
|
|
||||||
|
## Preparation:
|
||||||
The unedited conversation with Chat GPT, concerning almost all of the aspects of this session:
|
The unedited conversation with Chat GPT, concerning almost all of the aspects of this session:
|
||||||
(optional): read this to get a better understanding of the topic:
|
(optional): read this to get a better understanding of the topic:
|
||||||
https://chatgpt.com/share/67f18460-1c1c-8010-bc57-9f3b683ec87a
|
https://chatgpt.com/share/67f18460-1c1c-8010-bc57-9f3b683ec87a
|
||||||
|
|
||||||
# Branching
|
## 🚧Branching
|
||||||
- [ ] Create the feature/transportation-search branch based on develop
|
- [ ] Create the feature/transportation-search branch based on develop
|
||||||
|
|
||||||
# DTO
|
## DTO
|
||||||
In order to develop transportation search flow, three DTOs need to be created in the application layer.
|
In order to develop transportation search flow, three DTOs need to be created in the application layer.
|
||||||
|
|
||||||
- [ ] Create DTOs related to transportation search flow
|
- [ ] Create DTOs related to transportation search flow
|
||||||
|
|
||||||
📂 Suggested Folder: Application/DTOs/City`
|
📂 Suggested Folder: Application/DTOs/City`
|
||||||
|
|
||||||
|
|
||||||
```cs
|
```cs
|
||||||
public class CityDto
|
public class CityDto
|
||||||
{
|
{
|
||||||
@@ -23,34 +24,35 @@ public class CityDto
|
|||||||
```
|
```
|
||||||
|
|
||||||
📂 Suggested Folder: Application/DTOs/Transportation
|
📂 Suggested Folder: Application/DTOs/Transportation
|
||||||
|
|
||||||
```cs
|
```cs
|
||||||
public class TransportationSearchRequestDto
|
public class TransportationSearchRequestDto
|
||||||
{
|
{
|
||||||
public short? VehicleTypeId { get; init; }
|
public short? VehicleTypeId { get; init; }
|
||||||
public int? FromCityId { get; init; }
|
public int? FromCityId { get; init; }
|
||||||
public int? ToCityId { get; init; }
|
public int? ToCityId { get; init; }
|
||||||
public DateTime? StartDate { get; init; }
|
public DateTime? StartDate { get; init; }
|
||||||
public DateTime? EndDate { get; init; }
|
public DateTime? EndDate { get; init; }
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```cs
|
```cs
|
||||||
public class TransportationSearchResultDto
|
public class TransportationSearchResultDto
|
||||||
{
|
{
|
||||||
public long Id { get; init; }
|
public long Id { get; init; }
|
||||||
public required string CompanyTitle { get; init; }
|
public int VehicleTypeId { get; init; }
|
||||||
public required string FromLocationTitle { get; init; }
|
public string? VehicleTitle { get; init; }
|
||||||
public required string ToLocationTitle { get; init; }
|
public required string CompanyTitle { get; init; }
|
||||||
public required string FromCityTitle { get; init; }
|
public required string FromLocationTitle { get; init; }
|
||||||
public required string ToCityTitle { get; init; }
|
public required string ToLocationTitle { get; init; }
|
||||||
public DateTime StartDateTime { get; init; }
|
public required string FromCityTitle { get; init; }
|
||||||
public DateTime? EndDateTime { get; init; }
|
public required string ToCityTitle { get; init; }
|
||||||
public decimal Price { get; init; }
|
public DateTime StartDateTime { get; init; }
|
||||||
}
|
public DateTime? EndDateTime { get; init; }
|
||||||
|
public decimal Price { get; init; }
|
||||||
|
public int RemainingCapacity { get; init; }
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
# Repository
|
## Repository
|
||||||
There are a few things to be add to some repositories for transportation search flow.
|
There are a few things to be add to some repositories for transportation search flow.
|
||||||
|
|
||||||
- [ ] Create DTOs related to transportation search flow
|
- [ ] Create DTOs related to transportation search flow
|
||||||
@@ -108,7 +110,7 @@ public class TransportationRepository :
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
# Auto Mapper
|
## Auto Mapper
|
||||||
Auto Mapper simplifies mapping between aggregates and DTOs in both directions.
|
Auto Mapper simplifies mapping between aggregates and DTOs in both directions.
|
||||||
|
|
||||||
- [ ] Create a `MappingProfile` that inherits `Profile`, and use it to add configurations for mappings
|
- [ ] Create a `MappingProfile` that inherits `Profile`, and use it to add configurations for mappings
|
||||||
@@ -157,7 +159,7 @@ public class MappingProfile : Profile
|
|||||||
.
|
.
|
||||||
```
|
```
|
||||||
|
|
||||||
# Result & Result Status
|
## Result & Result Status
|
||||||
|
|
||||||
- [ ] Create `ResultStatus` enum and `Result` class
|
- [ ] Create `ResultStatus` enum and `Result` class
|
||||||
📂 Suggested Folder: Application/Result
|
📂 Suggested Folder: Application/Result
|
||||||
@@ -220,22 +222,18 @@ public enum ResultStatus
|
|||||||
You can read more about enums: [W3Schools](https://www.w3schools.com/cs/cs_enums.php)
|
You can read more about enums: [W3Schools](https://www.w3schools.com/cs/cs_enums.php)
|
||||||
|
|
||||||
|
|
||||||
# `IService` & Service
|
## `IService` & Service
|
||||||
|
|
||||||
Now, use `I[Entity]Repositry` and `IUnitOfWork` in services to implement business logic
|
Now, use `I[Entity]Repositry` and `IUnitOfWork` in services to implement business logic
|
||||||
|
|
||||||
- [ ] Create `I[Entity]Service` and `[Entity]Service` which implements it
|
- [ ] Create `I[Entity]Service` and `[Entity]Service` which implements it
|
||||||
|
|
||||||
📂 Suggested Folder for `I[Entity]Service`: Application/Interfaces
|
📂 Suggested Folder for `I[Entity]Service`: Application/Interfaces
|
||||||
|
|
||||||
📂 Suggested Folder for Services: Application/Services
|
📂 Suggested Folder for Services: Application/Services
|
||||||
|
|
||||||
|
|
||||||
- existence of an interface for each service class is optional
|
- existence of an interface for each service class is optional
|
||||||
- services can have multiple repositories in them -> logic-based structure
|
- services can have multiple repositories in them -> logic-based structure
|
||||||
|
|
||||||
An example of `I[Entity]Service`:
|
An example of `I[Entity]Service`:
|
||||||
|
|
||||||
```c#
|
```c#
|
||||||
public interface ITransportationService
|
public interface ITransportationService
|
||||||
{
|
{
|
||||||
@@ -244,7 +242,6 @@ public interface ITransportationService
|
|||||||
```
|
```
|
||||||
|
|
||||||
An example of `[Entity]Service`:
|
An example of `[Entity]Service`:
|
||||||
|
|
||||||
```cs
|
```cs
|
||||||
public class TransportationService : ITransportationService
|
public class TransportationService : ITransportationService
|
||||||
{
|
{
|
||||||
@@ -291,7 +288,7 @@ builder.Services.AddScoped<ICityService, CityService>();
|
|||||||
.
|
.
|
||||||
```
|
```
|
||||||
|
|
||||||
# Controller
|
## Controller
|
||||||
Now we're getting to endpoints, you should communicate with client side through web-api. So every controller uses Services in Application layer to receive requests and send responses with DTOs.
|
Now we're getting to endpoints, you should communicate with client side through web-api. So every controller uses Services in Application layer to receive requests and send responses with DTOs.
|
||||||
|
|
||||||
- [ ] Create an APIController (right click on the folder, and then under Add, select Controller, and then make sure to select the APIController type)
|
- [ ] Create an APIController (right click on the folder, and then under Add, select Controller, and then make sure to select the APIController type)
|
||||||
@@ -336,17 +333,16 @@ public class TransportationController : ControllerBase
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- HttpGet: handles a GET request from client -> important for routing
|
- `HttpGet`: handles a GET request from client -> important for routing
|
||||||
- Ok, BadRequest, NotFound and StatusCode are Json results to send through api
|
- Ok, `BadRequest`, `NotFound` and `StatusCode` are json results to send through API
|
||||||
- Use TransportationService to communicate with Application
|
- Use `TransportationService` to communicate with Application
|
||||||
|
|
||||||
# Inserting Sample Data
|
## Inserting Sample Data
|
||||||
|
|
||||||
For testing purposes, add some data into the related tables.
|
For testing purposes, add some data into the related tables.
|
||||||
You are provided with a SQL script, that adds some sample data into the following tables
|
You are provided with a SQL script, that adds some sample data into the following tables
|
||||||
|
|
||||||
**Important Notes:** Note that different database names, and different table names will produce errors while executing the script. Consider adjusting these names before executing the script
|
**Important Notes:** Note that different database names, and different table names will produce errors while executing the script. Consider adjusting these names before executing the script
|
||||||
|
|
||||||
- Cities
|
- Cities
|
||||||
- Companies
|
- Companies
|
||||||
- LocationTypes
|
- LocationTypes
|
||||||
@@ -356,8 +352,14 @@ You are provided with a SQL script, that adds some sample data into the followin
|
|||||||
- Transportation
|
- Transportation
|
||||||
|
|
||||||
- [ ] Open `TransportationRelatedSampleData.sql` with SSMS, and execute the query
|
- [ ] Open `TransportationRelatedSampleData.sql` with SSMS, and execute the query
|
||||||
|
> Note: there has been changes in database structure since this note and this file has been written.
|
||||||
|
|
||||||
|
|
||||||
# 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
|
||||||
|
|||||||
Reference in New Issue
Block a user