我正在创建一个以员工和雇主作为域对象的应用程序。他们都有一个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”并发送验证邮件。
这是我现在的代码。我正在使用Asp Net.Core.Identity.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;
}
以下是我的问题:
像这样:
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作为参数传递?
提前感谢您的回答。
从我看来,User
,Employee
和Employer
是聚合根(AR)。
根据DDD,这段代码和我的方法是否合适?
在DDD中,不建议聚合引用除by ID之外的其他聚合。你的Employee
和Employer
AR有这么糟糕的参考,所以不行。相反,Employee
和Employer
应该只包含一个UserId
字段。
我应该将员工的创建提取到域名服务吗?或者工厂?如果是这样,我应该从那里调用存储库方法? (我的意思是服务当然)
从我所看到的,你有一个创建多个聚合的复杂过程。在DDD中,您不能在单个事务中以原子方式执行此操作。相反,每个Aggregate都在自己的事务中创建/变异。然而,有一种协调长期过程的战术模式:Saga /流程经理。
您应该定义一个将员工注册为Saga的过程:RegisterEmployee
。这个过程应该有这些方法的接口:create
,start
,continue
。 create
方法接收开始处理所需的所有数据。 start
方法尝试运行各个步骤(如createEmployee,createUser等);如果start
方法再次运行,它应该从停止的地方继续,所以Saga应该记录它的状态。
通过将Aggregates上的命令设置为幂等,可以使架构更好。这样,当Saga重新启动时,它可以再次将所有命令发送给Aggregates;这有效地使Saga非常简单。
假设应该将员工的创建提取到域服务。我应该在内部创建用户吗?
该域名服务实际上是上一步的Saga。但是Saga不应该包含属于Aggregates的逻辑!小心不要使你的领域模型贫血。佐贺应该只包含协调逻辑!
最后一个问题:检查我想要创建的用户是否存在的正确位置在哪里?申请服务是否适合这样做?
什么意味着用户已经存在?已有用户使用该用户名?如果是,那么最简单的解决方案是在username
列上有一个唯一索引,如果可能的话。如果不可能(即你启用了分片),那么你可以让另一个Saga检查重复项并向管理员或其他人报告。