我正在创建一个以员工和雇主为域对象的应用程序。 他们两个都有对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;
}
这是我的问题:
赞:
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作为参数传递?
预先感谢您的回答。
答案 0 :(得分:0)
据我所见,User
,Employee
和Employer
是聚合根(AR)。
根据DDD,此代码和我的方法是否还可以?
在DDD中,不建议某个聚合引用除by ID以外的其他聚合。您的Employee
和Employer
AR具有这样的 bad 引用,因此不正确。相反,Employee
和Employer
应该仅包含UserId
字段。
我应该将创建员工的工作提取到域服务中吗?也许是工厂?如果可以,我应该从那里调用存储库方法吗? (我的意思是服务)
据我所知,您具有创建多个聚合的复杂过程。在DDD中,您不能在单个事务中自动执行此操作。相反,每个聚合都在其自己的事务中创建/变异。但是,有一个协调长流程的战术模式:Saga /流程经理。
您应该定义一个将员工注册为Saga的过程:RegisterEmployee
。该过程应具有以下方法的接口:create
,start
,continue
。 create
方法接收启动过程所需的所有数据。 start
方法尝试运行各个步骤(例如createEmployee,createUser等);如果再次运行start
方法,它应该从停止的地方继续,因此Saga应该记录其状态。
通过将“聚合”上的命令设为幂等,可以使体系结构更好。这样,当Saga重新启动时,它可以再次将所有命令发送到聚合;这实际上使Saga非常简单。
让我们说应该将员工的创建提取到域服务中。那我应该在内部创建用户吗?
该域服务实际上是上一步中的Saga。然而,传奇不应包含属于集合的逻辑!小心不要使域模型贫乏。传奇应该只包含协调逻辑!
最后一个问题:在哪里检查我要创建的用户是否存在?应用程序服务是否适合这样做?
什么表示一个用户已经存在?已经有一个使用该用户名的用户了吗?如果是,那么最简单的解决方案是,在可能的情况下,在username
列上具有唯一索引。如果不可能(即已启用分片),则可以使用另一个Saga检查重复项并向管理员报告。