域驱动设计中的存储库
本文关键字:存储 | 更新日期: 2023-09-27 17:59:56
几个月来,我一直在与DDD作斗争,虽然我认为我对一些概念有一个相当好的想法,但我对实现应该如何工作,特别是应该如何从数据库加载数据不太有信心。我在C#只工作了六个月,这也于事无补。
[原始问题-见下文更新]
在我开始开发的应用程序中,我有一个包含基类的域命名空间,一个使用这些基类执行操作的服务命名空间,以及一个连接到数据库的存储库和DAL命名空间。
我认为最简单的方法是在服务命名空间中使用继承来添加LoadFromDb等过程,但当我开始实现时,我发现这个方法通常需要最多的代码,因为我必须两次分配所有类属性(一次在存储库命名空间中,然后在服务命名空间)。
下面是一个例子。我可以让选项2和选项3发挥作用,但我希望在精神上更接近选项1。
namespace Domain
{
public class Request
{
public int RequestID{get; set;}
public string RequestingUser {get; set;}
public string Title {get; set;}
public string Description{get; set;}
public string status {get; set;}
}
}
namespace app
{
class MyApp
{
void Main()
{
//option 1
Domain.Request x = new Service.svcRequest(5);
//option 2
Domain.Request y = new Service.svcRequest(5);
//option 3
Domain.Request z = new Domain.Request();
Service.svcRequest2.loadRequest(5, z);
}
}
}
namespace Service
{
public class svcRequest : Domain.Request
{
public svcRequest(int RequestID)
{
//this is what I want to do.
// It fails because "this" is read-only
// and because "this" can't be implicitly converted to DomainRequest.
this = (Domain.Request)repos.Loads.LoadRequest(RequestID);
//option 2, which is what I'm doing instead for now, but when you get
// to 20 or 50 properties, it's a bit much,
// esp. since those properties have already been assigned once
// within the repository namespace.
Domain.Request MyRequest = repos.Loads.LoadRequest(RequestID);
this.RequestID = MyRequest.RequestID;
this.RequestingUser = MyRequest.RequestingUser;
this.Title = MyRequest.Title;
this.Description = MyRequest.Description;
this.status = MyRequest.status;
}
}
public class svcRequest2
{
//option 3. Much less code, but now I'm not really using inheritance,
// so in my application layer I can't just declare my variable
// and use the svcRequest constructor
public static void loadRequest(int RequestID, Domain.Request MyRefRequest)
{
MyRefRequest = (Domain.Request)repos.Loads.LoadRequest(RequestID);
}
}
}
namespace repos
{
public static class Loads
{
public static Domain.Request LoadRequest(int RequestID)
{
Domain.Request MyRequest = new Domain.Request();
DataRow MyRow = dal.Loads.LoadRequestRow(RequestID);
MyRequest.RequestID = RequestID;
MyRequest.RequestingUser = (string)MyRow["User"];
MyRequest.Title = (string)MyRow["Title"];
MyRequest.Description = (string)MyRow["Description"];
MyRequest.status = (string)MyRow["Status"];
return MyRequest;
}
}
}
namespace dal
{
public static class Loads
{
public static DataRow LoadRequestRow(int RequestID)
{
OleDbConnection dbCon = new OleDbConnection("Provider=SQLOLEDB;Data Source=dbServer;Initial Catalog=RequestDB;User ID=Joe;Password=password");
string Select = "Select * from RequestTable where ID = " + RequestID;
OleDbDataAdapter dbRequest = new OleDbDataAdapter(Select, dbCon);
DataSet dsRequest = new DataSet();
dbRequest.Fill(dsRequest);
DataRow drRequest = dsRequest.Tables[0].Rows[0];
return drRequest;
}
}
}
[更新]这是第二次尝试。我将Domain命名空间重命名为Model,并将Service命名空间重命名为Domain,我认为这更符合DDD约定。按照建议,我在DAL中使用的存储库名称空间中添加了一个接口。我现在唯一不能做的是选项1的Load语句,但我认为我只需要进一步研究继承。
我是不是越来越近了?
using System;
using System.Data;
using System.Data.OleDb;
namespace Model
{
public class Request
{
public int RequestID{get; set;}
public string RequestingUser {get; set;}
public string Title {get; set;}
public string Description{get; set;}
public string status {get; set;}
}
}
namespace App
{
class MyApp
{
void Main()
{
//option 1
Model.Request x = new Domain.dmnRequest(5);
//option 2
Model.Request y = new Domain.dmnRequest2(5);
//option 3
Model.Request z = new Model.Request();
Domain.dmnRequest3.loadRequest(5, z);
}
}
}
namespace Domain
{
public class dmnRequest : Model.Request, dal.Request
{
public dmnRequest(int requestID)
{
//this is what I want to do. I'm not sure why it's failing
Load(requestID);
}
}
public class dmnRequest2 : Model.Request
{
public dmnRequest2(int requestID)
{
//option 2; it works but is cumbersome after you hit the 20th property
dal.Request tmpRequest = new dal.Request();
tmpRequest.Load(requestID);
this.RequestID = tmpRequest.RequestID;
this.RequestingUser = tmpRequest.RequestingUser;
this.Title = tmpRequest.Title;
this.Description = tmpRequest.Description;
this.status = tmpRequest.status;
}
}
public class dmnRequest3
{
//option 3. Much less code, but now I'm not really using inheritance, so in my application layer I can't just declare my variable and use the dmnRequest constructor
public static void loadRequest(int RequestID, Model.Request MyRequest)
{
dal.Request dalRequest = (dal.Request)MyRequest;
dalRequest.Load(RequestID);
MyRequest = (Model.Request)dalRequest;
}
}
}
namespace repos
{
public interface SaveMe {void Save(int ID); }
public interface LoadMe {void Load(int ID); }
}
namespace dal
{
public class Request : Model.Request, repos.LoadMe
{
public void Load(int requestID)
{
OleDbConnection dbCon = new OleDbConnection("yaddayadda");
string Select = "Select * from RequestTable where ID = " + requestID.ToString();
OleDbDataAdapter dbRequest = new OleDbDataAdapter(Select, dbCon);
DataSet dsRequest = new DataSet();
dbRequest.Fill(dsRequest);
DataRow drRequest = dsRequest.Tables[0].Rows[0];
this.RequestID = requestID;
this.RequestingUser = (string)drRequest["User"];
this.Title = (string)drRequest["Title"];
this.Description = (string)drRequest["Description"];
this.status = (string)drRequest["Status"];
}
}
}
你错了。在DDD中,事情非常简单。Domain只知道存储库接口,该接口由持久层(DAL)中的实际存储库类实现。存储库与数据库一起保存/加载域对象(在DDD中,这些域对象应该是聚合根)。
这里不应该是静态的,存储库应该从数据库中获取所需的所有数据,然后使用这些数据来恢复对象。存储库总是返回域实体,而不是数据行、数据表、实体框架实体等。这是因为存储库的目的是将域与持久性细节解耦。
简单地说,域只是说:"嘿,存储库给我这个id的BsuitnessEntity"。域告诉存储库要获取什么,而从不如何获取。域实际上并不知道涉及数据库。它所看到的只是一个抽象(存储库接口),用于处理域所知道的对象。
所有这一切的重点是尊重关注点分离。Domain关心业务概念和用例,而存储库关心从数据库存储/检索对象。