260 lines
8.8 KiB
Markdown
260 lines
8.8 KiB
Markdown
## *Should I have an `IEntityService` and then `EntityService` for each of my entities?**
|
||
|
||
Not necessarily **for _every_** entity — only if it **makes sense**.
|
||
- 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.
|
||
- Otherwise, for basic operations, **directly using a repository (via a unit of work or interface)** from the use case handler might be fine.
|
||
## **Is it OK to have services not related to a specific entity?**
|
||
|
||
Absolutely, **yes**. In fact, that’s expected in a Clean Architecture setup.
|
||
|
||
Examples:
|
||
- A `ReportGenerationService` that combines bookings, customers, and payments.
|
||
- A `TokenService` for authentication tokens.
|
||
- A `CurrencyConversionService` that hits an external API.
|
||
- 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.
|
||
|
||
## **Is it necessary to have an interface for each service?**
|
||
|
||
## **What’s the Difference Between Services and Repositories?**
|
||
|
||
| Aspect | **Service** | **Repository** |
|
||
| ------------------ | ---------------------------------------------------------------------- | --------------------------------------------------------------- |
|
||
| **Layer** | Application Layer | Domain Layer (interface), Infrastructure Layer (implementation) |
|
||
| **Responsibility** | **Orchestrates business logic** / use cases | **Data access abstraction** |
|
||
| **Focus** | Coordinates multiple domain/repo operations, validation, business flow | Fetching/storing data for a specific entity |
|
||
| **Example** | `PlaceOrderService`, `ReportService` | `ICustomerRepository`, `IOrderRepository` |
|
||
|
||
## 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**.
|
||
|
||
|
||
## Should I use `class` or `record` for DTOs in Clean Architecture?
|
||
|
||
### 🔵 Short answer:
|
||
|
||
> **Use `record` for DTOs when possible** — it's clean, immutable by default, and semantically perfect for data transfer.
|
||
|
||
---
|
||
|
||
### 🔍 Why `record` is a great fit for DTOs
|
||
|
||
| Feature | `record` | `class` |
|
||
| ----------------------- | ----------------- | ---------------------------- |
|
||
| Immutable by default | ✅ (with `init`) | ❌ (need manual setup) |
|
||
| Value-based equality | ✅ | ❌ (ref-based by default) |
|
||
| Concise syntax | ✅ | ❌ (more boilerplate) |
|
||
| Use for data containers | ✅ (perfect fit) | ✅ (but more verbose) |
|
||
| Custom behavior/logic | ❌ (less suitable) | ✅ (better for rich behavior) |
|
||
### But when should you prefer `class`?
|
||
|
||
Use `class` if your DTO or model:
|
||
- Needs to be **mutable** after creation
|
||
- Has to **interact with legacy APIs/libraries**
|
||
- Needs **inheritance or polymorphism** (not well supported in `record`)
|
||
- 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.
|
||
|
||
## RESTful APIs:
|
||
[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?
|
||
|
||
### Key Principles of REST:
|
||
|
||
1. **Statelessness**:
|
||
|
||
- Each API call must contain all the information the server needs to fulfill the request (no session state). Each request is independent.
|
||
|
||
2. **Resource Identification**:
|
||
|
||
- Resources (e.g., customers, orders) should be identified using URIs. Use nouns in URIs, not verbs.
|
||
|
||
3. **HTTP Methods**:
|
||
|
||
- Use standard HTTP methods to represent actions:
|
||
|
||
- **GET**: Retrieve a resource.
|
||
|
||
- **POST**: Create a new resource.
|
||
|
||
- **PUT**: Update a resource entirely.
|
||
|
||
- **PATCH**: Update a resource partially.
|
||
|
||
- **DELETE**: Remove a resource.
|
||
|
||
4. **Use of Standard Status Codes**:
|
||
|
||
- Return appropriate HTTP status codes (e.g., `200 OK`, `201 Created`, `404 Not Found`, `500 Internal Server Error`).
|
||
|
||
5. **HATEOAS**:
|
||
|
||
- (Hypermedia as the Engine of Application State) - Provide links to related resources within the responses.
|
||
|
||
|
||
### Multiple GET Methods in One Controller:
|
||
|
||
- **Yes, you can have multiple GET methods in one controller**. The key is to differentiate them based on routes and parameters.
|
||
|
||
- For example:
|
||
|
||
|
||
```c#
|
||
[ApiController]
|
||
[Route("api/[controller]")]
|
||
public class CustomerController : ControllerBase {
|
||
|
||
[HttpGet("{id}")]
|
||
public IActionResult GetCustomerById(int id) { /*...*/ }
|
||
|
||
[HttpGet]
|
||
public IActionResult GetAllCustomers() { /*...*/ }
|
||
|
||
[HttpGet("{id}/orders")]
|
||
public IActionResult GetCustomerOrders(int id) { /*...*/ }
|
||
}
|
||
```
|
||
|
||
|
||
- **Routing**: ASP.NET Core uses route templates to differentiate these actions. The combination of route parameters, query strings, and action names can help separate the GET requests.
|
||
|
||
|
||
## List-like stuff in `C#`
|
||
They all serve similar purposes—holding multiple items—but differ in functionality, performance, and use cases. Here’s a detailed breakdown:
|
||
|
||
🔷 1. `IEnumerable`
|
||
- Namespace: System.Collections.Generic
|
||
- Most basic "list-like" abstraction.
|
||
- Read-only (forward-only iteration).
|
||
- You can use foreach on it.
|
||
- 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.
|
||
|
||
Example:
|
||
|
||
```csharp
|
||
IEnumerable<int> numbers = GetNumbers(); // Lazy-loaded maybe
|
||
foreach (var num in numbers)
|
||
Console.WriteLine(num);
|
||
```
|
||
|
||
💡 Ideal when:
|
||
- You want to return a sequence without exposing modification.
|
||
- You’re using LINQ chains.
|
||
- You’re returning data from a database query.
|
||
|
||
---
|
||
|
||
🔷 2. ICollection
|
||
- Extends IEnumerable.
|
||
- Adds Count and Add/Remove/Clear methods.
|
||
- Still abstract—List and HashSet implement it.
|
||
|
||
💡 Useful when:
|
||
- You want to expose a collection that can be modified (e.g. Add or Remove).
|
||
- You care about the Count.
|
||
|
||
|
||
---
|
||
|
||
🔷 3. `IList`
|
||
- Extends `ICollection` and `IEnumerable`.
|
||
- Adds index access: `list[0]` etc.
|
||
- Think of it like a mutable array with dynamic size.
|
||
|
||
💡 Use when:
|
||
|
||
- You want ordered collection with indexing.
|
||
|
||
- You need to insert, remove, or replace items at specific positions.
|
||
|
||
|
||
---
|
||
|
||
🔷 4. List
|
||
|
||
- A concrete class (not interface).
|
||
- Implements `IList`, `ICollection`, `IEnumerable`.
|
||
- Backed by an array (auto-resizes).
|
||
- Fast read and write.
|
||
- Supports `Add`, `Remove`, `Insert`, `IndexOf`, etc.
|
||
|
||
Example:
|
||
|
||
```csharp
|
||
var list = new List<string>();
|
||
list.Add("One");
|
||
list.Add("Two");
|
||
var second = list[1]; // "Two"
|
||
```
|
||
|
||
💡 Go-to general purpose collection.
|
||
|
||
---
|
||
|
||
🔷 5. `IReadOnlyCollection` & `IReadOnlyList`
|
||
|
||
- `IReadOnlyCollection`: Just Count and `IEnumerable`.
|
||
- `IReadOnlyList`: Adds indexing without modification.
|
||
- Used to expose lists safely (read-only).
|
||
|
||
💡 Used when:
|
||
- You want to return a list, but prevent any changes.
|
||
|
||
---
|
||
|
||
🔷 6. `Array(T[])`
|
||
|
||
- Fixed-size.
|
||
- Fastest for indexing.
|
||
- Cannot change size.
|
||
- Implements `IList` (via Array).
|
||
|
||
Example:
|
||
|
||
```csharp
|
||
int[] numbers = new int[5];
|
||
numbers[0] = 42;
|
||
```
|
||
|
||
💡 Great for performance-sensitive or fixed-size data.
|
||
|
||
---
|
||
|
||
🔷 7. `ObservableCollection`
|
||
|
||
- For WPF/`Blazor`/WinForms data-binding.
|
||
- Notifies UI when items are added/removed.
|
||
- Implements IList.
|
||
|
||
💡 Use in UI apps when the view needs to react to collection changes.
|
||
|
||
---
|
||
|
||
🔷 8. HashSet
|
||
- Unordered, no duplicates.
|
||
- Implements `ICollection`, not `IList`.
|
||
- No index access.
|
||
|
||
💡 Best for fast membership checking (contains x).
|
||
|
||
---
|
||
|
||
🔷 Summary Table
|
||
|
||
|Type|Indexing|Modifiable|Ordered|Duplicates|Use Case|
|
||
|---|---|---|---|---|---|
|
||
|IEnumerable|❌|❌|✔️|✔️|Basic read-only stream or LINQ chaining|
|
||
|ICollection|❌|✔️|✔️|✔️|Expose modifiable group of items|
|
||
|IList|✔️|✔️|✔️|✔️|Full-featured list abstraction|
|
||
|List|✔️|✔️|✔️|✔️|Standard go-to collection|
|
||
|IReadOnlyList|✔️|❌|✔️|✔️|Return data safely from service or repo|
|
||
|Array (T[])|✔️|✔️|✔️|✔️|Fixed-size, performant indexing|
|
||
|ObservableCollection|✔️|✔️|✔️|✔️|UI binding for collections|
|
||
|HashSet|❌|✔️|❌|❌|Fast lookup, uniqueness enforcement|
|
||
|