在DDD中,viewmodel如何重用领域模型行为?
我不确定,但只要我读到有关 ddd 的内容,域模型就永远不应该离开应用程序层..如果这是真的,那么视图模型如何重用域模型的行为?
假设 ddd 视角下有以下发票模型
public class Invoice
{
public int Id { get; set; }
public int CustomerID { get; internal set; }
public void ChangeCustomer(Customer customer)
{
if (customer.IsActive == false)
throw new Exception("Inactive customers cannot be used");
CustomerID = customer.Id;
//other properties change also that need to be reflected to the user interface
}
}
现在让发票 ViewModel 尝试#1。按照这个想法,我在重用域行为方面没有问题,但域层必须引用本例中的 UI 项目(WPF)。但在这里我担心我们不应该在应用程序层之外使用域层
public class InvoiceVMOption1 : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyUI(string PropertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
}
Invoice underlinesource;
public InvoiceVMOption1(Invoice underlinesource)
{
this.underlinesource = underlinesource;
}
public int InvoiceID { get => underlinesource.Id; }
public int CustomerID
{
get => underlinesource.CustomerID;
set
{
try
{
//This is a very simple example. in reality when changing customer other properties of the domain change also.
var customer = new CustomerService().Get(value);
underlinesource.ChangeCustomer(customer);
NotifyUI(nameof(CustomerID));
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
throw;
}
}
}
}
现在让我们拥有发票视图模型选项#2 按照这个想法,这意味着应用程序服务负责构建视图模型并将其提供给 UI,然后 UI 返回视图模型 > 转换为域 > 视图模型。通过存储库更新
/// <summary>
/// I like more this idea as it decouple viewmodel from domain layer
/// The problem here is how i can reuse the logic of changing the customer since domain model is not referenced
/// </summary>
public class InvoiceVMOption2 : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyUI(string PropertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
}
private int invoiceid;
public int InvoiceID
{
get => invoiceid;
set
{
invoiceid = value;
NotifyUI(nameof(InvoiceID));
}
}
private int customerid;
public int CustomerID
{
get => customerid;
set
{
//user select a customer from combobox binding with his id
//here we need to let the user whenever any of the validation that exists in domain model invoice will fail when changing customer.
//nothing will save yet
//after user finish this viewmodel will be sent to application layer(service) convert it to domain and update through repository
//Problem is, how i can know if customer will fail to change without writing same code validation from domain?
customerid = value;
NotifyUI(nameof(customerid));
}
}
}
I am not sure but as long i read about ddd, domain model should never leave the application layer.. if that is true then how viewmodels can reuse behavior of domain model?
Assume the following invoice model in ddd perspective
public class Invoice
{
public int Id { get; set; }
public int CustomerID { get; internal set; }
public void ChangeCustomer(Customer customer)
{
if (customer.IsActive == false)
throw new Exception("Inactive customers cannot be used");
CustomerID = customer.Id;
//other properties change also that need to be reflected to the user interface
}
}
And now lets have the invoice ViewModel attempt #1. Going with this idea i have no issues to reuse domain behavior, but the domain layer must be referenced to the UI Project in this case(WPF). But is here where my fears appear that we should not use domain layer outside of application layer
public class InvoiceVMOption1 : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyUI(string PropertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
}
Invoice underlinesource;
public InvoiceVMOption1(Invoice underlinesource)
{
this.underlinesource = underlinesource;
}
public int InvoiceID { get => underlinesource.Id; }
public int CustomerID
{
get => underlinesource.CustomerID;
set
{
try
{
//This is a very simple example. in reality when changing customer other properties of the domain change also.
var customer = new CustomerService().Get(value);
underlinesource.ChangeCustomer(customer);
NotifyUI(nameof(CustomerID));
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
throw;
}
}
}
}
And now lets have Invoice ViewModel option #2
Going with this idea, it means application service is responsible to construct the viewmodel and give it to the UI and then UI give back viewmodel >convert to domain > update throught repository
/// <summary>
/// I like more this idea as it decouple viewmodel from domain layer
/// The problem here is how i can reuse the logic of changing the customer since domain model is not referenced
/// </summary>
public class InvoiceVMOption2 : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyUI(string PropertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
}
private int invoiceid;
public int InvoiceID
{
get => invoiceid;
set
{
invoiceid = value;
NotifyUI(nameof(InvoiceID));
}
}
private int customerid;
public int CustomerID
{
get => customerid;
set
{
//user select a customer from combobox binding with his id
//here we need to let the user whenever any of the validation that exists in domain model invoice will fail when changing customer.
//nothing will save yet
//after user finish this viewmodel will be sent to application layer(service) convert it to domain and update through repository
//Problem is, how i can know if customer will fail to change without writing same code validation from domain?
customerid = value;
NotifyUI(nameof(customerid));
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
视图模型是一条单向街道。它只是为了从域表示构建适合 UI 的数据结构。
UI 上的每个更改最好建模为单独的命令(如果您使用的是 CQRS)或调用特定应用程序服务的不同 API(如果您使用的是核心 DDD)。
通过选择对每个更改进行建模更改后,您可以在代码中更准确地表示域。如果您要返回对数据结构进行修改的相同视图模型,则您将隐藏这些更改的意图。流程模式也更接近 CRUD,因为您只需将数据传输到后端进行持久化。
The View Model is a one-way street. It is only meant to construct a data structure suited to the UI from the domain representation.
Each change at the UI is better modeled as a separate Command (if you are using CQRS) or a different API that calls a specific Application Service (if you are using core DDD).
By choosing to model each change, you represent the domain more accurately in your code. If you were to return the same view model with modifications to the data structure, you are hiding the intent of these changes. The flow pattern is also closer to CRUD because you are simply transferring data to the backend to be persisted.