POCO的,DTO的,DLL的和贫血领域模型
-
23-08-2019 - |
题
我一直在寻找在(似乎POCO和DTO 之间的差异在于POCO的是DTO与行为(方法?))和跨 。
通过缺乏理解,我想我已经创建了这些贫血的域模型之一。
在我的应用我有一个“DTO的” DLL中定义我的业务领域实体。他们有很多与吸气的和setter的,别无他的属性。我的业务逻辑代码(填充,计算)是在另一个“血铅”的dll,我的数据访问代码是在“达尔”的DLL。 '最佳实践' 我想。
所以通常创建一个DTO像这样:
dto.BusinessObject bo = new dto.BusinessObject(...)
和它传递给像这样BLL层:
bll.BusinessObject.Populate(bo);
继而,执行某些逻辑,并将其传递到像这样的dal层:
dal.BusinessObject.Populate(bo);
从我的理解,使我DTO的成POCO的我需要的对象的业务逻辑和行为(方法)的一部分。因此,而不是代码的上面它更像:
poco.BusinessObject bo = new poco.BusinessObject(...)
bo.Populate();
即。我打电话在物体上的方法,而不是使所述对象的方法。
我的问题是 - 我怎么能做到这一点,仍然保留的关注“最佳实践”分层(单独的DLL的等等)。不调用对象的方法意味着,该方法必须在对象定义
请帮我的困惑。
解决方案
通常情况下,你不希望引入到持久化你的域对象,因为它不是商业模式的一部分(飞机不构造本身,它飞乘客/货物从一个地方到另一个)。您应该使用库模式,一个的 ORM框架或一些其他数据访问模式来管理对象的状态的持久存储和retreival。
凡贫血域模型也发挥了作用是当你正在做的事情是这样的:
IAirplaneService service = ...;
Airplane plane = ...;
service.FlyAirplaneToAirport(plane, "IAD");
在这种情况下,飞机的状态管理(无论是飞行,在那里它在,什么发车时间/机场,什么是到达时间/机场,有什么飞行计划等)委托给一些外部平面...的AirplaneService实例。
实现,这将是设计您接口这样的方式POCO:
Airplane plane = ...;
plane.FlyToAirport("IAD");
这是更容易发现,因为开发商知道去哪里找,使飞机飞(只是告诉飞机这样做)。它还允许您以确保状态的只有的内部管理。然后,您可以使之类的东西的当前位置为只读,并确保它在一个地方才改变。与贫血域对象,因为状态被设置在外部,发现其中状态改变变得越来越困难作为您域规模增大。
其他提示
我想澄清这最好的方法是通过定义:
<强> DTO:数据传输对象:强>
它们仅用于数据传输典型地表示层和服务层之间。无外乎以上。通常它被实现为具有获取并设置类。
public class ClientDTO
{
public long Id {get;set;}
public string Name {get;set;}
}
<强> BO:业务对象:强>
业务对象代表的商业元素和自然是最好的做法说,他们应该包含业务逻辑也。如通过迈克尔草甸说,它也是好的做法来隔离该对象中的数据的访问。
public class Client
{
private long _id;
public long Id
{
get { return _id; }
protected set { _id = value; }
}
protected Client() { }
public Client(string name)
{
this.Name = name;
}
private string _name;
public string Name
{
get { return _name; }
set
{ // Notice that there is business logic inside (name existence checking)
// Persistence is isolated through the IClientDAO interface and a factory
IClientDAO clientDAO = DAOFactory.Instance.Get<IClientDAO>();
if (clientDAO.ExistsClientByName(value))
{
throw new ApplicationException("Another client with same name exists.");
}
_name = value;
}
}
public void CheckIfCanBeRemoved()
{
// Check if there are sales associated to client
if ( DAOFactory.Instance.GetDAO<ISaleDAO>().ExistsSalesFor(this) )
{
string msg = "Client can not be removed, there are sales associated to him/her.";
throw new ApplicationException(msg);
}
}
}
<强>服务或应用程序类强> 这些类表示用户和该系统之间的交互,他们将利用两个ClientDTO和客户端的
public class ClientRegistration
{
public void Insert(ClientDTO dto)
{
Client client = new Client(dto.Id,dto.Name); /// <-- Business logic inside the constructor
DAOFactory.Instance.Save(client);
}
public void Modify(ClientDTO dto)
{
Client client = DAOFactory.Instance.Get<Client>(dto.Id);
client.Name = dto.Name; // <--- Business logic inside the Name property
DAOFactory.Instance.Save(client);
}
public void Remove(ClientDTO dto)
{
Client client = DAOFactory.Instance.Get<Client>(dto.Id);
client.CheckIfCanBeRemoved() // <--- Business logic here
DAOFactory.Instance.Remove(client);
}
public ClientDTO Retrieve(string name)
{
Client client = DAOFactory.Instance.Get<IClientDAO>().FindByName(name);
if (client == null) { throw new ApplicationException("Client not found."); }
ClientDTO dto = new ClientDTO()
{
Id = client.Id,
Name = client.Name
}
}
}
我个人不觉得那些贫血的领域模型如此糟糕;我真的很喜欢具有仅代表数据,而不是行为域对象的想法。我觉得这种方法的主要缺点是代码的可发现性;你需要知道,可使用它们哪些动作。要解决这个问题,仍然保持从模型中分离行为代码的方法之一是引入了行为的接口:
interface ISomeDomainObjectBehaviour
{
SomeDomainObject Get(int Id);
void Save(SomeDomainObject data);
void Delete(int Id);
}
class SomeDomainObjectSqlBehaviour : ISomeDomainObjectBehaviour
{
SomeDomainObject ISomeDomainObjectBehaviour.Get(int Id)
{
// code to get object from database
}
void ISomeDomainObjectBehaviour.Save(SomeDomainObject data)
{
// code to store object in database
}
void ISomeDomainObjectBehaviour.Delete(int Id)
{
// code to remove object from database
}
}
class SomeDomainObject
{
private ISomeDomainObjectBehaviour _behaviour = null;
public SomeDomainObject(ISomeDomainObjectBehaviour behaviour)
{
}
public int Id { get; set; }
public string Name { get; set; }
public int Size { get; set; }
public void Save()
{
if (_behaviour != null)
{
_behaviour.Save(this);
}
}
// add methods for getting, deleting, ...
}
这样,你可以继续从模型中分离出来的行为实施。使用接口实现的被注射到模型中也使代码而易于测试,因为可以很容易地嘲笑的行为。