当前位置:网站首页>Implement Domain Driven Design - use ABP framework - create entities

Implement Domain Driven Design - use ABP framework - create entities

2022-06-24 12:50:00 Broadm

Use case demonstration - Create entities

This section demonstrates some sample use cases and discusses alternative scenarios .

Create entities

From entity / Aggregating the root class to create objects is the first step in the entity life cycle . polymerization / Aggregate root rules and best practices section The suggestion is Entity class Create a primary constructor , In order to make sure Create a valid entity . therefore , Whenever we need to create an instance of an entity , We should all Use that constructor

See the following question aggregate root classes :

public class Issue : AggregateRoot<Guid>
{
    public Guid RepositoryId { get; private set; }
    public string Title { get; private set; }
    public string Text { get; set; }
    public Guid? AssignedUserId { get; private set; }
    
    public Issue(
        Guid id, 
        Guid repositoryId,
        string title,
        string text = null
    ) : base(id)
    {
        RepositoryId = repositoryId;
        Title = Check.NotNullOrWhiteSpace(title, nameof(title));
        Text = text; //  Allow null value 
    }

    private Issue() { // by ORM Reserved empty constructor  }

    public void SetTitle(string title)
    {
        Title = Check.NotNullOrWhiteSpace(title, nameof(title));
    }
}
  • This class is guaranteed by its constructor Create a valid entity .

  • If you need to change the title , You need to use SetTitle Method Ensure that the title is in a valid state

  • If you want to assign this question to a user , You need to use IssueManager ( It implements some business rules before allocation , Please refer to my previous discussion about Field service The article ).

  • Text Property has a public setter, Because it also accepts null value , And this example doesn't have any validation rules . It is also optional in the constructor

Let's look at the Application Service Method :

public class IssueAppService : ApplicationService, IIssueAppService
{
    // omitted Repository and DomainService Dependency injection of 

    [Authorize]
    public async Task<IssueDto> CreateAsync(IssueCreationDto input)
    {
        // Create a valid problem entity 
        var issue = new Issue(
            GuidGenerator.Create(),
            input.RepositoryId,
            input.Title,
            input.Text
        );

        // If an assignee is passed in , Then assign the problem method to this user 
        if(input.AssignedUserId.HasValue)
        {
            var user = await _userRepository.GetAsync(input.AssignedUserId.Value);
            await _issueManager.AssignToAsync(issue, user);
        }

        //  Save the problem entity to the database 
        await _issueRepository.InsertAsync(issue);

        // Returns the... That represents the new problem DTO
        return ObjectMapper.Map<Issue, IssueDto>(issue);
    }
}

CreateAsync Method :

  • Use Issue Constructors create valid questions . It USES IGuidGenerator Service delivery Id. Automatic object mapping is not used here
  • If the client wants to assign this problem to the user when the object is created , It will use IssueManager To complete , allow IssueManager Perform the necessary checks before allocation .
  • Save entity to database
  • Finally using IObjectMapper Return to one IssueDto , The IssueDto It is through mapping from New Issue Entities are automatically created

Create entities using domain rules

The above example , Issue There are no business rules for entity creation , In addition to doing some form of validation in the constructor . however , In some cases , Entity creation should check some additional business rules

for example , Suppose you don't want to Exactly the same title Create a problem when a problem already exists . Where to implement this rule ? stay Application Service It is not appropriate to implement this rule in , Because it is one that should always be checked The core business ( field ) The rules

The rule should be in Field service ( In this case, it's IssueManager ) To realize . therefore , We need to force the application layer to always use IssueManager To create a new Issue

First , We can Issue Constructor set to internal , instead of public:

public class Issue : AggregateRoot<Guid>
{
    internal Issue(
        Guid id, 
        Guid repositoryId,
        string title,
        string text = null
    ) : base(id)
    {
        //...
    }
}

This prevents application services from using constructors directly , So they will use IssueManager . Then we can go to IssueManager Add a CreateAsync Method :

public class IssueManager : DomainService
{
    // Omit dependency injection 

    public async Task<IssueDto> CreateAsync(
        Guid repositoryId,
        string title,
        string text = null
    )
    {
        // If there is a problem with the same title , Direct throw 
        if(await _issueRepository.AnyAsync(i => i.Title == title))
        {
            throw new BusinessException("IssueTracking:IssueWithSameTitleExists");
        }

        // Create a valid problem entity 
        return new Issue(
            GuidGenerator.Create(),
            repositoryId,
            title,
            text
        );
    }
}
  • CreateAsync Method to check whether the same title already has a problem , And throw a business exception in this case
  • If there is no repetition , Create and return a new Issue

In order to use the above method ,IssueAppService It is modified as follows :

public class IssueAppService : ApplicationService, IIssueAppService
{
    // Omit dependency injection 

    public async Task<IssueDto> CreateAsync(IssueCreationDto input)
    {
        //* Modify to create a valid problem entity through domain services ,  Not directly new
        var issue = await _issueManager.CreateAsync(
            GuidGenerator.Create(),
            input.RepositoryId,
            input.Title,
            input.Text
        );

        // If an assignee is passed in , Then assign the problem method to this user 
        if(input.AssignedUserId.HasValue)
        {
            var user = await _userRepository.GetAsync(input.AssignedUserId.Value);
            await _issueManager.AssignToAsync(issue, user);
        }

        //  Save the problem entity to the database 
        await _issueRepository.InsertAsync(issue);

        // Returns the... That represents the new problem DTO
        return ObjectMapper.Map<Issue, IssueDto>(issue);
    }
}

Discuss : Why doesn't the problem lie in IssueManager Save to database ?

You may ask “ Why? IssueManager Do not save the problem to the database ?” We think it's Responsibility for application services

because , Before saving the problem object , Application services may need to make additional changes to them / operation . If the domain service saves it , The save operation will be repeated

  • Two round trips to the database can result in performance loss
  • An explicit database transaction is required to contain these two operations
  • If due to business rules , Other operations cancel entity creation , The transaction should be rolled back in the database

When you check IssueAppService when , You will see it in IssueManager.CreateAsync Don't save Issue To the database . otherwise , We will need to perform an insert ( stay IssueManager in ) And an update ( After the assignment question )

Discuss : Why not implement duplicate Title Checking in application services ?

We can simply say “ Because it's a Core domain logic , It should be implemented in the domain layer ”. However , This brings a new problem : “ How do you determine that it is the core domain logic , Not application logic ?” ( We will discuss the differences in detail later )

For this example , A simple question can help us make a decision : “ If we had another way ( Use cases ) To create a problem , Whether we still apply the same rules ?” You might think “ Why do we have a second way to create problems ?” However , in real life , Do you have :

  • The end user of the application may be in the standard UI Create a problem in ( For example github Web page creation problem )
  • You may have a second Background applications , Used by your own employees , You might want to provide a way to create problems ( In this case, different authorization rules may be used )
  • You may have a pair of Third party clients Open HTTP API, They create problems .
  • You may have a background worker service, If it detects some faults , It does something and creates problems . such , It will work without any user interaction ( There may not be any standard authorization checks ) Create questions .
  • You can even go to UI Set a button on the , Put some content ( for example , Discuss ) transformation For the problem

in summary , Different applications always follow these rules : The title of a new question cannot be the same as that of any existing question ! They have nothing to do with the application layer ! This is why this logic is the core domain logic , Should be in the domain layer , It should not be implemented as duplicate code in application services .

原网站

版权声明
本文为[Broadm]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/175/202206241033314200.html