英文:
Aggregate initialization with required fields in Domain Driven Design
问题
以下是您要翻译的内容:
我有以下模型:
public class Invoice{
public Invoide(Guid customerId, string customerName){
CustomerId = customerId;
CustomerName = customerName;
}
[Required]
public Guid Id{get;set;}
[Required]
public Guid CustomerId {get;set;}
[Required(AllowEmptyStrings=false)]
// The name is also stored to be independend from future customer changes
public string CustomerName {get;set;}
public IEnumerable<InvoiceItem> Items {get;set;}
}
public class InvoiceCreate: Dto{
public Guid CustomerId{get;set;}
}
要创建新的发票,我有以下类:
public class InvoiceService : IInvoiceService{
public InvoiceService(
IInvoiceRepository invoiceReposiory,
ICustomerRepository customerRepository
){
_invoiceRepository = invoiceRepository;
_customerRepository = customerRepository;
}
public async Task<Invoice> CreateAsync(Invoice invoice){
...
}
}
public class InvoicesController{
public InvoicesController(IInvoiceService invoiceService){
...
}
public async Task<IActionResult> CreateInvoiceAsync([FromBody] InvoiceCreate createDto)
{
Invoice invoice = new Invoice(
createDto.CustomerId,
???? // CustomerName?
)
}
}
在这种情况下,我不确定在哪里填写CustomerName
,因为它是从另一个存储库加载的,而不是来自请求。
根据领域驱动设计,必须在初始化时设置客户名称,因为它是一个必填字段。这意味着我需要在控制器中加载客户,并且服务必须信任“控制器层”这将是正确的。(否则,如果服务还加载客户,就会有重复的数据库请求)
另一个解决方案是将必填字段保持为空,并在初始化后在InvoiceService中填充它。但这将违反DDD模式并导致不一致的发票模型。此外,“DateTimes”不能保持为空。
什么是正确的解决方案以实现清晰和稳定的过程?
英文:
I have the following model:
public class Invoice{
public Invoide(Guid customerId, string customerName){
CustomerId = customerId;
CustomerName = customerName;
}
[Required]
public Guid Id{get;set;}
[Required]
public Guid CustomerId {get;set;}
[Required(AllowEmptyStrings=false)]
// The name is also stored to be independend from future customer changes
public string CustomerName {get;set;}
public IEnumerable<InvoiceItem> Items {get;set;}
}
public class InvoiceCreate: Dto{
public Guid CustomerId{get;set;}
}
To create a new invoice I have the following classes:
public class InvoiceService : IInvoiceService{
public InvoiceService(
IInvoiceRepository invoiceReposiory,
ICustomerRepository customerRepository
){
_invoiceRepository = invoiceRepository;
_customerRepository = customerRepository;
}
public async Task<Invoice> CreateAsync(Invoice invoice){
...
}
}
public class InvoicesController{
public InvoicesController(IInvoiceService invoiceService){
...
}
public async Task<IActionResult> CreateInvoiceAsync([FromBody] InvoiceCreate createDto)
{
Invoice invoice = new Invoice(
createDto.CustomerId,
???? // CustomerName?
)
}
}
In this scenario I'm not sure where to fill the CustomerName
because it is loaded from another repository and doesn't come from Request.
According to the domain driven design the customer name must be set on initialization because it is an required field. This would mean, that I need to load the customer in the Controller and the service must trust the "Controller-Layer" that this would be fine. (Otherwise if the service also loads the customer, I have duplicate DB-Requests)
The other solution would be to keep the required field empty and fill it in InvoiceService after initialization. But this would contradict the DDD Pattern and lead to an inconstitent invoice Model. In addition to that "DateTimes" cannot be kept empty.
What is the right solution to have a clean and stable procedure?
答案1
得分: 1
您的发票服务在CreateAsync方法中不应该期望一个Invoice对象。您应该使用请求的DTO。
public class InvoiceService : IInvoiceService{
public InvoiceService(
IInvoiceRepository invoiceRepository,
ICustomerRepository customerRepository
){
_invoiceRepository = invoiceRepository;
_customerRepository = customerRepository;
}
public async Task<Invoice> CreateAsync(InvoiceCreate invoiceCreate){
Customer customer = await _customerRepository.FindAsync(invoiceCreate.CustomerId);
Invoice invoice = new Invoice(customer.Id, customer.Name);
await _invoiceRepository.AddAsync(invoice);
}
}
或者,在Invoice的构造函数中接受一个Customer实体:
public class Invoice{
public Invoice(Customer customer){
CustomerId = customer.Id;
CustomerName = customer.Name;
}
}
然后在您的服务中使用:
```csharp
public async Task<Invoice> CreateAsync(InvoiceCreate invoiceCreate){
Customer customer = await _customerRepository.FindAsync(invoiceCreate.CustomerId);
Invoice invoice = new Invoice(customer);
await _invoiceRepository.AddAsync(invoice);
}
与此同时,您的控制器应负责提供API端点,并在将DTO传递给服务之前应用安全检查:
public class InvoicesController{
public InvoicesController(IInvoiceService invoiceService){
...
}
[Authorize]
public async Task<IActionResult> CreateInvoiceAsync([FromBody] InvoiceCreate createDto)
{
Invoice invoice = await _invoiceService.CreateAsync(createDto);
}
}
英文:
Your Invoice service should not expect an Invoice in CreateAsync. You should use the request Dto.
public class InvoiceService : IInvoiceService{
public InvoiceService(
IInvoiceRepository invoiceReposiory,
ICustomerRepository customerRepository
){
_invoiceRepository = invoiceRepository;
_customerRepository = customerRepository;
}
public async Task<Invoice> CreateAsync(InvoiceCreate invoiceCreate){
Customer customer = await _customerRepository.FindAsync(invoiceCreate.CustomerId);
Invoice invoice = new Invoice(customer.Id, customer.Name);
await _invoiceRepository.AddAsync(invoice);
}
}
Alternatively, accept a Customer entity in the constructor of the Invoice:
public class Invoice{
public Invoide(Customer customer){
CustomerId = customer.Id;
CustomerName = customer.Name;
}
and then in your service use:
public async Task<Invoice> CreateAsync(InvoiceCreate invoiceCreate){
Customer customer = await _customerRepository.FindAsync(invoiceCreate.CustomerId);
Invoice invoice = new Invoice(customer);
await _invoiceRepository.AddAsync(invoice);
}
In the meantime, your controller should be responsible for providing an Api endpoint and applying security checks before passing the Dto down to the service:
public class InvoicesController{
public InvoicesController(IInvoiceService invoiceService){
...
}
[Authorize]
public async Task<IActionResult> CreateInvoiceAsync([FromBody] InvoiceCreate createDto)
{
Invoice invoice = await _invoiceService.CreateAsync(createDto);
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论