刚刚在SO上创建了一个acc来询问:)
假设这个简化的例子:构建一个Web应用程序来管理项目......
该应用程序具有以下要求/规则。
1)用户应该能够创建插入项目名称的项目 2)项目名称不能为空 3)两个项目不能具有相同的名称。
我使用的是4层架构(用户界面,应用程序,域,基础架构) 在我的应用层上,我有以下ProjectService.cs类:
public class ProjectService
{
private IProjectRepository ProjectRepo { get; set; }
public ProjectService(IProjectRepository projectRepo)
{
ProjectRepo = projectRepo;
}
public void CreateNewProject(string name)
{
IList<Project> projects = ProjectRepo.GetProjectsByName(name);
if (projects.Count > 0) throw new Exception("Project name already exists.");
Project project = new Project(name);
ProjectRepo.InsertProject(project);
}
}
在我的域层,我有Project.cs类和IProjectRepository.cs接口:
public class Project
{
public int ProjectID { get; private set; }
public string Name { get; private set; }
public Project(string name)
{
ValidateName(name);
Name = name;
}
private void ValidateName(string name)
{
if (name == null || name.Equals(string.Empty))
{
throw new Exception("Project name cannot be empty or null.");
}
}
}
public interface IProjectRepository
{
void InsertProject(Project project);
IList<Project> GetProjectsByName(string projectName);
}
在我的Infrastructure层,我有IProjectRepository的实现,它执行实际的查询(代码无关紧要)。
我不喜欢这个设计的两件事:
1)我已经读过,存储库接口应该是域的一部分,但实现不应该。这对我来说没有任何意义,因为我认为域不应该调用存储库方法(持久性无知),这应该是应用程序层中服务的责任。 (有些东西告诉我,我非常错误。)
2)创建新项目的过程涉及两个验证(非空且不重复)。在我上面的设计中,这两个验证分散在两个不同的地方,这使得更难以看到最新情况。
所以,我的问题是,从DDD的角度来看,这是正确建模还是以不同的方式进行?
答案 0 :(得分:2)
创建新项目的过程涉及两个验证(非空且不重复)。在我上面的设计中,这两个验证分散在两个不同的地方,这使得更难以看到最新情况。
项目不能也不应该知道应用程序中的所有项目(项目本身不应该知道列表中的所有其他项目),因此 - 域服务(instead of application service)负责。埃文斯书了解确切的区别)。
验证有很多种。并且there can't be universal validation mechanism。 DDD只是说你必须在领域模型中进行域验证。
答案 1 :(得分:1)
我认为与(1)混淆的部分原因是你缺少一个图层 - 在你的架构中插入一个服务层,你的问题就像魔法一样消失了。您可以将服务和存储库实现放在服务层中 - 即,您有一个使用存储库的具体实现的服务。如果需要,其他服务可以自由选择存储库的替代实现。您的应用程序可以自由选择它喜欢的任何服务接口。话虽如此,我不确定在大多数情况下它真的很重要。在我的几乎所有应用程序中,我都有一个基本修复的“域/数据层”。我可能会根据业务逻辑的复杂程度对存储库进行分层。与服务相同 - 如果项目不是很复杂,可能根本就没有必要。如果它变得如此晚,我总能重构。通常我会将我的存储库放在与我的数据上下文相同的项目中(使用LINQ),如果有服务,它将在一个单独的项目中(因为它通常也会作为Web服务公开)。 / p>
关于(2),您需要从并发角度考虑问题。如果可能,最好通过数据库约束来检查重复名称。我认为这是强制执行此逻辑的最简单方法。在尝试插入之前,您当然可以检查是否存在重复,但除非您在事务中执行此操作,否则无法保证不会出现另一个进程并在您的检查和插入之间插入一个进程。数据库约束解决了这个问题。将检查移动到插入逻辑(相同的事务)也解决了问题,但无论我认为您需要准备好将其作为插入失败以及(或代替)验证错误来处理它。