域对象的创建

时间:2018-09-11 09:33:57

标签: c# asp.net-core domain-driven-design

我正在创建一个以员工和雇主为域对象的应用程序。 他们两个都有对User对象的引用,我在其中存储密码和其他与帐户有关的东西。

示例:

public class Employee
{
    public Guid EmployeeId { get; set; }
    public User User { get; set; }
    public string Name { get; set; }
    public string Surname { get; set; }
    public string About { get; set; }
    ...
    //other properties
}

public class Employer
{
    public Guid EmployerId { get; set; }
    public User User { get; set; }
    public string CompanyName { get; set; }
    public string CompanyDescription { get; set; }
    public string FoundedYear { get; set; }
    ...
    //other properties
}

public class User
    public Guid UserId { get; set; }
    public string Email { get; set; }
    public string PasswordHash { get; set; }
    ...
   //other properties
}

我还在使用应用程序服务,其中一种方法代表一个用例。 假设我有RegisterEmpolyee方法,该方法应将员工保存到数据库,并将其角色设置为“ Employee”并发送验证电子邮件。

这是我的代码。我正在使用AspNet.Core.Idenity.UserManager创建用户帐户:

    public async Task<EmployeeDto> RegisterEmployee(RegisterEmployeeDto employee)
    {
        var validateResult = _validatorService.Validate(employee);
        if (!validateResult.IsValid)
            throw new ServerException
                ("RegisterEmployeeDto is not valid", validateResult.GetErrors());

        await _db.BeginTransactionAsync();
        var newUser = new User { UserName = employee.Email, Email = employee.Email };
        var userCreationResult = await _userManager.CreateAsync(newUser, employee.Password);

        if (!userCreationResult.Succeeded)
        {
            var userCreationErrors = userCreationResult.GetIdentityResultErrors();
            throw new ServerException("Error during create User account.", userCreationErrors);
        }

        await _roleService.AddUserToRoleAsync(newUser.Id, ApplicationRoles.Employee);
        var verificationCode = await _userManager.GenerateEmailConfirmationTokenAsync(newUser);
        newUser.VerificationCode = verificationCode;

        await _emailService.SendActivationEmail(newUser.Email, newUser.Id, verificationCode);

        var newEmployee = new Employee(employee.Name, employee.Surname, newUser);

        await _db.Employees.AddAsync(newEmployee);
        await _db.CompleteAsync();

        var employeeDto = _mapper.Map<Employee, EmployeeDto>(newEmployee);

        _db.CommitTransaction();

        return employeeDto;
    }

这是我的问题:

  1. 根据DDD,此代码和我的方法是否还可以?
  2. 我应该将创建员工的工作提取到域服务中吗?也许是工厂?如果可以,我应该从那里调用存储库方法吗? (我的意思是服务)
  3. 让我们说应该将员工的创建提取到域服务中。那我应该在内部创建用户吗?

赞:

    public async Task<Employee> CreateEmployee(RegisterEmployeeDto employee)
    {
        var newUser = new User { UserName = employee.Email, Email = employee.Email };
        var userCreationResult = await _userManager.CreateAsync(newUser, employee.Password);

        if (!userCreationResult.Succeeded)
        {
            var userCreationErrors = userCreationResult.GetIdentityResultErrors();
            throw new ServerException("Error during create User account.", userCreationErrors);
        }

        var newEmployee = new Employee(employee.Name, employee.Surname, newUser);
        //Should I call repository here? 
        await _db.Employees.AddAsync(newEmployee);
        await _db.CompleteAsync();

        return newEmployee;
    }

还是可以将User作为参数传递?

  1. 最后一个问题:在哪里检查我要创建的用户是否存在?应用程序服务是否适合这样做?

预先感谢您的回答。

1 个答案:

答案 0 :(得分:0)

据我所见,UserEmployeeEmployer是聚合根(AR)。

  

根据DDD,此代码和我的方法是否还可以?

在DDD中,不建议某个聚合引用除by ID以外的其他聚合。您的EmployeeEmployer AR具有这样的 bad 引用,因此不正确。相反,EmployeeEmployer应该仅包含UserId字段。

  

我应该将创建员工的工作提取到域服务中吗?也许是工厂?如果可以,我应该从那里调用存储库方法吗? (我的意思是服务)

据我所知,您具有创建多个聚合的复杂过程。在DDD中,您不能在单个事务中自动执行此操作。相反,每个聚合都在其自己的事务中创建/变异。但是,有一个协调长流程的战术模式:Saga /流程经理。

您应该定义一个将员工注册为Saga的过程:RegisterEmployee。该过程应具有以下方法的接口:createstartcontinuecreate方法接收启动过程所需的所有数据。 start方法尝试运行各个步骤(例如createEmployee,createUser等);如果再次运行start方法,它应该从停止的地方继续,因此Saga应该记录其状态。

通过将“聚合”上的命令设为幂等,可以使体系结构更好。这样,当Saga重新启动时,它可以再次将所有命令发送到聚合;这实际上使Saga非常简单。

  

让我们说应该将员工的创建提取到域服务中。那我应该在内部创建用户吗?

该域服务实际上是上一步中的Saga。然而,传奇不应包含属于集合的逻辑!小心不要使域模型贫乏。传奇应该只包含协调逻辑!

  

最后一个问题:在哪里检查我要创建的用户是否存在?应用程序服务是否适合这样做?

什么表示一个用户已经存在?已经有一个使用该用户名的用户了吗?如果是,那么最简单的解决方案是,在可能的情况下,在username列上具有唯一索引。如果不可能(即已启用分片),则可以使用另一个Saga检查重复项并向管理员报告。