我在最近的项目中使用Domain driven N-layered application architecture与EF code first
,我在Repository
图层中定义了Domain
个合同。
使其他Repositories
更简洁的基本合同:
public interface IRepository<TEntity, in TKey> where TEntity : class
{
TEntity GetById(TKey id);
void Create(TEntity entity);
void Update(TEntity entity);
void Delete(TEntity entity);
}
每个Repositories
都有专门的Aggregation root
,例如:
public interface IOrderRepository : IRepository<Order, int>
{
IEnumerable<Order> FindAllOrders();
IEnumerable<Order> Find(string text);
//other methods that return Order aggregation root
}
如您所见,所有这些方法都依赖于Domain entities
。
但在某些情况下,应用程序的UI
需要一些不是Entity
的数据,这些数据可能来自两个或更多肠炎的数据(View-Model
s),在这些情况下,我在View-Model
中定义Application layer
,因为它们非常依赖于Application's
的需求,而不是Domain
。
所以,我认为我有两种方法可以在View-Models
中将数据显示为UI
:
Repository
仅取决于Entities
,并在我要向用户展示时将Repositories
的方法的结果映射到View-Models
(在{{ 1}}通常)。Application Layer
添加一些方法,将结果直接返回Repositories
,然后在View-Models
中使用这些返回的值,然后Application Layer
(这些专门的{{ 1}}我称之为UI
的合同,放入Repositories
,与放入Readonly Repository Contract
的其他Application Layer
'合同不同。
假设,我的Repositories
需要Domain
,其中包含3或4个属性(来自3或4 大 UI
)。
它的数据可以通过简单的投影生成,但在第一种情况下,由于我的方法无法访问View-Model
,我必须有时获取所有3或4个表的所有字段,巨大的连接,然后将结果映射到Entities
。
但是,在案例2中,我可以简单地使用投影并直接填充View-Models
。
所以,从性能的角度来看,我认为案例2优于案例1.但我认为View-Models
应该依赖于View-Model
而不是Repository
的设计点图。
有没有更好的方法不会导致Entities
图层依赖于View-Models
,也没有达到性能?或者,对于阅读查询,Domain
取决于Application layer
?(案例2)
答案 0 :(得分:21)
也许使用 command-query separation (在应用程序级别)可能会有所帮助。
您应该使您的存储库仅依赖于实体,并且只在您的存储库中保留普通检索方法 - 即 GetOrderById() - 以及create /当然更新/合并/删除)。想象一下,实体,存储库,域服务,用户界面命令,处理这些命令的应用程序服务(例如,处理Web应用程序中的POST请求的某个Web控制器等)代表您的写模型,应用程序的写入方。
然后构建一个单独的读取模型,可以像你想的那样脏 - 把5个表的连接放在那里,从文件中读取宇宙中恒星数量的代码,将它相乘以A开头的书籍数量(在对亚马逊进行查询之后)并建立一个n维结构等等 - 你明白了:)但是,在阅读模型上,不要添加任何处理的代码修改你的实体。您可以从此读取模型中自由返回所需的任何视图模型,但可以从此处触发任何数据更改。
读写分离应该会降低程序的复杂性并使一切变得更易于管理。而且您可能也会发现它不会违反您在问题中提到的设计规则(希望如此)。
从性能的角度来看,使用读取模型,即将读取数据的代码与写入/更改<的代码分开编写/ strong>数据是你能得到的最好的:)这是因为你甚至可以在那里破坏一些SQL代码而不会在晚上睡不着觉 - 如果写得好,SQL查询将为你的应用程序提供相当大的速度提升。
Nota bene:我开玩笑说你可以编写读取方的代码 - 读取端代码应该像干净一样简单,当然是写入端代码: )
此外,如果需要,您可以删除通用存储库接口,因为它只会混淆您正在建模的域,并强制每个具体的存储库公开不必要的方法:)请参阅{ {3}}。例如,很可能Delete()方法永远不会用于 OrderRepository - 因为,或许永远不应该删除Orders(当然,它总是依赖于它)。当然,您可以将数据库行管理原语保存在单个模块中,并在具体存储库中重用这些原语,但不要将这些原语暴露给除了存储库的实现之外的任何其他原语 - 只是因为在其他任何地方都不需要它们,如果公开暴露,可能会使醉酒的程序员感到困惑。
最后,也许不考虑域层,应用层,数据层或查看模型也是有益的图层过于严格。请阅读this根据实际意义/目的(或功能)打包您的软件模块比基于不自然打包它们要好一些,< em>难以理解的,难以解释为5岁的孩子标准,即将它们逐层打包
答案 1 :(得分:1)
好吧,对我来说,我会将ViewModel映射到Model对象并使用我的存储库中的那些来进行读/写,因为您可能知道有几种工具可以为此做,在我个人的情况下我使用{{ 3}}我发现很容易实现。
尽量保持Web层和Repository层之间的依赖关系尽可能分开,说repos应该只与模型对话,并且你的web层应该与你的视图模型对话。
一个选项可能是您可以在服务中使用DTO并自动化Web层中的这些对象(可能是一对一映射的情况),缺点是您最终可能会遇到很多样板代码和dtos和视图模型可能会感觉重复。
另一种选择是返回模型中的部分水合物体,并将这些物体作为DTO展示,并将这些物体映射到您的视图模型,这个解决方案可能有点模糊,但您可以进行所需的投影,只返回信息你需要的。
你可以摆脱视图模型并在你的web层中公开dtos,并将它们用作视图模型,减少代码,但更多耦合方法。
答案 2 :(得分:1)
我有点同意佩德罗在这里。使用应用服务层可能是有益的。如果您的目标是MVVM类型的实现,我建议创建一个Model类,负责保存使用服务层检索的数据。如果您的实体,DTO和模型一致地命名(因此您不必编写大量手动映射),那么使用automapper映射数据是一个非常好的主意。
根据我的经验,在viewmodels中使用您的实体/ poco来显示数据会导致大量的泥浆。不同的视图有不同的需求,所有这些都需要为实体添加更多属性。慢慢地使您的查询更复杂,更慢。
如果您的数据没有改变,通常您可能需要考虑引入(sql / database)视图,这些视图会将一些繁重的工作转移到数据库(高度优化的地方)。 EF可以很好地处理数据库视图。然后检索实体并将数据(从视图)映射到模型或DTO变得相当简单。