Skip to content

Commit

Permalink
Merge branch 'dev' into salihozkara/license
Browse files Browse the repository at this point in the history
  • Loading branch information
salihozkara committed Dec 24, 2024
2 parents c663ebd + 7c93b7c commit 51d2ba3
Show file tree
Hide file tree
Showing 53 changed files with 315 additions and 124 deletions.
4 changes: 4 additions & 0 deletions docs/en/Community-Articles/2022-09-15-Grpc-Demo/POST.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,3 +240,7 @@ gRPC on .NET has different approaches, features, configurations and more details
* You can find the completed source code here: https://github.com/abpframework/abp-samples/tree/master/GrpcDemo2

* You can also see all the changes I've done in this article here: https://github.com/abpframework/abp-samples/pull/200/files

## See Also

* [Consuming gRPC Services from Blazor WebAssembly Application Using gRPC-Web](https://abp.io/community/articles/consuming-grpc-services-from-blazor-webassembly-application-using-grpcweb-dqjry3rv)
Original file line number Diff line number Diff line change
Expand Up @@ -187,11 +187,21 @@ On the other hand, resolving keyed services from `LazyServiceProvider` is not su

### Automatically Registering Keyed Services

Currently, if you want to register a keyed service, you need to do it manually as we see in the previous sections by using one of the overloads (`.AddKeyedTransient`, `.AddKeyedScoped` and `.AddKeyedSingleton`).
ABP provides the `ExposeKeyedServiceAttribute` to control which keyed services are provided by the related class.

It would be good if we could make this process automatically and not need to manually register services, and for that purpose, I have [created an issue](https://github.com/abpframework/abp/issues/18794) that aims to introduce an attribute, which allows us to automatically register multiple services as keyed services.
For example, if you want to register a keyed service as a transient dependency, you can do it as follows:

You can [follow the issue](https://github.com/abpframework/abp/issues/18794) if you are considering using keyed services in your application and don't want to register them manually.
```csharp
[ExposeKeyedService<ITaxCalculator>("taxCalculator")]
[ExposeKeyedService<ICalculator>("calculator")]
public class TaxCalculator: ICalculator, ITaxCalculator, ICanCalculate, ITransientDependency
{
}
```

> Notice that the ExposeKeyedServiceAttribute only exposes the keyed services. So, you can not inject the ITaxCalculator or ICalculator interfaces in your application without using the FromKeyedServicesAttribute as shown in the example above. If you want to expose both keyed and non-keyed services, you can use the ExposeServicesAttribute and ExposeKeyedServiceAttribute attributes altogether.
Please refer to the [Dependency Injection document](https://abp.io/docs/latest/framework/fundamentals/dependency-injection#exposekeyedservice-attribute) for further info.

## Summary

Expand Down
68 changes: 68 additions & 0 deletions docs/en/docs-nav.json
Original file line number Diff line number Diff line change
Expand Up @@ -934,6 +934,74 @@
{
"text": "Microservices",
"path": "framework/architecture/microservices"
},
{
"text": "Module Development Best Practices",
"items": [
{
"text": "Overview",
"path": "framework/architecture/best-practices"
},
{
"text": "Module Architecture",
"path": "framework/architecture/best-practices/module-architecture.md"
},
{
"text": "Domain Layer",
"items": [
{
"text": "Overview",
"path": "framework/architecture/best-practices/domain-layer-overview.md"
},
{
"text": "Entities",
"path": "framework/architecture/best-practices/entities.md"
},
{
"text": "Repositories",
"path": "framework/architecture/best-practices/repositories.md"
},
{
"text": "Domain Services",
"path": "framework/architecture/best-practices/domain-services.md"
}
]
},
{
"text": "Application Layer",
"items": [
{
"text": "Overview",
"path": "framework/architecture/best-practices/application-layer-overview.md"
},
{
"text": "Application Services",
"path": "framework/architecture/best-practices/application-services.md"
},
{
"text": "Data Transfer Objects",
"path": "framework/architecture/best-practices/data-transfer-objects.md"
}
]
},
{
"text": "Data Access",
"items": [
{
"text": "Overview",
"path": "framework/architecture/best-practices/data-access-overview.md"
},
{
"text": "Entity Framework Core Integration",
"path": "framework/architecture/best-practices/entity-framework-core-integration.md"
},
{
"text": "MongoDB Integration",
"path": "framework/architecture/best-practices/mongodb-integration.md"
}
]
}
]
}
]
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
# Application Services Best Practices & Conventions

> This document offers best practices for implementing Application Services classes in your modules and applications based on Domain-Driven-Design principles.
>
> **Ensure you've read the [*Application Services*](../domain-driven-design/application-services.md) document first.**
## General

* **Do** create an application service for each **aggregate root**.

### Application Service Interface
## Application Service Interface

* **Do** define an `interface` for each application service in the **application contracts** package.
* **Do** inherit from the `IApplicationService` interface.
Expand All @@ -11,11 +17,11 @@
* **Do not** get/return entities for the service methods.
* **Do** define DTOs based on the [DTO best practices](data-transfer-objects.md).

#### Outputs
### Outputs

* **Avoid** to define too many output DTOs for same or related entities. Instead, define a **basic** and a **detailed** DTO for an entity.

##### Basic DTO
#### Basic DTO

**Do** define a **basic** DTO for an aggregate root.

Expand Down Expand Up @@ -44,7 +50,7 @@ public class IssueLabelDto
}
```

##### Detailed DTO
#### Detailed DTO

**Do** define a **detailed** DTO for an entity if it has reference(s) to other aggregate roots.

Expand Down Expand Up @@ -81,20 +87,20 @@ public class LabelDto : ExtensibleEntityDto<Guid>
}
````

#### Inputs
### Inputs

* **Do not** define any property in an input DTO that is not used in the service class.
* **Do not** share input DTOs between application service methods.
* **Do not** inherit an input DTO class from another one.
* **May** inherit from an abstract base DTO class and share some properties between different DTOs in that way. However, should be very careful in that case because manipulating the base DTO would effect all related DTOs and service methods. Avoid from that as a good practice.

#### Methods
### Methods

* **Do** define service methods as asynchronous with **Async** postfix.
* **Do not** repeat the entity name in the method names.
* Example: Define `GetAsync(...)` instead of `GetProductAsync(...)` in the `IProductAppService`.

##### Getting A Single Entity
#### Getting A Single Entity

* **Do** use the `GetAsync` **method name**.
* **Do** get Id with a **primitive** method parameter.
Expand All @@ -104,7 +110,7 @@ public class LabelDto : ExtensibleEntityDto<Guid>
Task<QuestionWithDetailsDto> GetAsync(Guid id);
````

##### Getting A List Of Entities
#### Getting A List Of Entities

* **Do** use the `GetListAsync` **method name**.
* **Do** get a single DTO argument for **filtering**, **sorting** and **paging** if necessary.
Expand All @@ -117,7 +123,7 @@ Task<QuestionWithDetailsDto> GetAsync(Guid id);
Task<List<QuestionWithDetailsDto>> GetListAsync(QuestionListQueryDto queryDto);
````

##### Creating A New Entity
#### Creating A New Entity

* **Do** use the `CreateAsync` **method name**.
* **Do** get a **specialized input** DTO to create the entity.
Expand Down Expand Up @@ -151,7 +157,7 @@ public class CreateQuestionDto : ExtensibleObject
}
````

##### Updating An Existing Entity
#### Updating An Existing Entity

- **Do** use the `UpdateAsync` **method name**.
- **Do** get a **specialized input** DTO to update the entity.
Expand All @@ -167,7 +173,7 @@ Example:
Task<QuestionWithDetailsDto> UpdateAsync(Guid id, UpdateQuestionDto updateQuestionDto);
````

##### Deleting An Existing Entity
#### Deleting An Existing Entity

- **Do** use the `DeleteAsync` **method name**.
- **Do** get Id with a **primitive** method parameter. Example:
Expand All @@ -176,7 +182,7 @@ Task<QuestionWithDetailsDto> UpdateAsync(Guid id, UpdateQuestionDto updateQuesti
Task DeleteAsync(Guid id);
````

##### Other Methods
#### Other Methods

* **Can** define additional methods to perform operations on the entity. Example:

Expand All @@ -186,7 +192,7 @@ Task<int> VoteAsync(Guid id, VoteType type);

This method votes a question and returns the current score of the question.

### Application Service Implementation
## Application Service Implementation

* **Do** develop the application layer **completely independent from the web layer**.
* **Do** implement application service interfaces in the **application layer**.
Expand All @@ -195,30 +201,30 @@ This method votes a question and returns the current score of the question.
* **Do** make all public methods **virtual**, so developers may inherit and override them.
* **Do not** make **private** methods. Instead make them **protected virtual**, so developers may inherit and override them.

#### Using Repositories
### Using Repositories

* **Do** use the specifically designed repositories (like `IProductRepository`).
* **Do not** use generic repositories (like `IRepository<Product>`).

#### Querying Data
### Querying Data

* **Do not** use LINQ/SQL for querying data from database inside the application service methods. It's repository's responsibility to perform LINQ/SQL queries from the data source.

#### Extra Properties
### Extra Properties

* **Do** use either `MapExtraPropertiesTo` extension method ([see](../../fundamentals/object-extensions.md)) or configure the object mapper (`MapExtraProperties`) to allow application developers to be able to extend the objects and services.

#### Manipulating / Deleting Entities
### Manipulating / Deleting Entities

* **Do** always get all the related entities from repositories to perform the operations on them.
* **Do** call repository's Update/UpdateAsync method after updating an entity. Because, not all database APIs support change tracking & auto update.

#### Handle files
### Handle files

* **Do not** use any web components like `IFormFile` or `Stream` in the application services. If you want to serve a file you can use `byte[]`.
* **Do** use a `Controller` to handle file uploading then pass the `byte[]` of the file to the application service method.

#### Using Other Application Services
### Using Other Application Services

* **Do not** use other application services of the same module/application. Instead;
* Use domain layer to perform the required task.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Data Transfer Objects Best Practices & Conventions

> This document offers best practices for implementing Data Transfer Object classes in your modules and applications based on Domain-Driven-Design principles.
>
> **Ensure you've read the [*Data Transfer Objects*](../domain-driven-design/data-transfer-objects.md) document first.**
## General

* **Do** define DTOs in the **application contracts** package.
* **Do** inherit from the pre-built **base DTO classes** where possible and necessary (like `EntityDto<TKey>`, `CreationAuditedEntityDto<TKey>`, `AuditedEntityDto<TKey>`, `FullAuditedEntityDto<TKey>` and so on).
* **Do** inherit from the **extensible DTO** classes for the **aggregate roots** (like `ExtensibleAuditedEntityDto<TKey>`), because aggregate roots are extensible objects and extra properties are mapped to DTOs in this way.
Expand Down
10 changes: 6 additions & 4 deletions docs/en/framework/architecture/best-practices/domain-services.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Domain Services Best Practices & Conventions

### Domain Service
> This document offers best practices for implementing Domain Service classes in your modules and applications based on Domain-Driven-Design principles.
>
> **Ensure you've read the [*Domain Services*](../domain-driven-design/domain-services.md) document first.**
## Domain Services

- **Do** define domain services in the **domain layer**.
- **Do not** create interfaces for the domain services **unless** you have a good reason to (like mock and test different implementations).
Expand All @@ -14,7 +18,7 @@ public class IssueManager : DomainService
}
```

### Domain Service Methods
## Domain Service Methods

- **Do not** define `GET` methods. `GET` methods do not change the state of an entity. Hence, use the repository directly in the Application Service instead of Domain Service method.

Expand Down Expand Up @@ -57,8 +61,6 @@ public async Task AssignToAsync(Issue issue, IdentityUser user)
- **Do not** return `DTO`. Return only domain objects when you need.
- **Do not** involve authenticated user logic. Instead, define extra parameter and send the related data of ` CurrentUser` from the Application Service layer.



## See Also

* [Video tutorial](https://abp.io/video-courses/essentials/domain-services)
Expand Down
Loading

0 comments on commit 51d2ba3

Please sign in to comment.