在 DDD 中访问持久性的位置
Where to access persistance in DDD
我正在研究领域驱动设计(是的,我来晚了),到目前为止我已经意识到领域模型应该是宇宙的中心。数据持久性只是一个实际的细节。
但是,如果这是真的,那么我很难弄清楚与持久性部分(例如存储库)的通信应该在哪里。
例如,如果我希望能够为学生添加新成绩,我是否应该像这样从学生域模型内部调用存储库?
interface IGradeRepository
{
void SaveGrade(int courseId, string grade);
// ...other methods
}
class Student
{
IGradeRepository _gradeRepository;
List<Grade> _grades = new List<Grade>();
public Student(IGradeRepository gradeRepository)
{
_gradeRepository = gradeRepository;
}
int StudentId { get; set; }
void AddGrade(int courseId, string grade)
{
var grade = new Grade(this.StudentId, courseId, grade);
_grades.Add(grade);
// Do I put the call to the data persistance here?
_gradeRepository.SaveGrade(grade);
}
}
如您所见,我最好的选择是从域模型内部访问存储库。但是,我不确定这是否是解决问题的方法。这似乎是许多教程中遗漏的内容之一。
总结一下:在进行领域驱动设计时,我从哪里访问数据层?
更新
正如某些人评论的那样,从我的示例中并不清楚我将如何访问学生 class。我可能会从视图模型或通过某种用例服务来做到这一点。
视图模型示例:
class StudentGradesViewModel
{
// (...) all sort of VM-stuff
private Student _student;
private Course _selectedCourse;
public void AddGrade(string grade)
{
_student.AddGrade(_course.CourseId, grade);
}
}
should I call the repository from inside the student domain model, like this?
这不是通常的模式。
相反,更常见的是与存储库的交互发生在应用层,而不是在域层。
存储库为应用程序提供了对聚合根的访问权限,并且应用程序仅与聚合根进行交互。所以简单的模式看起来像
using transaction():
Root root = _repository.get(rootId)
root.doSomeCoolDomainThing(...)
这里的假设是 root 可以访问维持其域不变所需的所有持久化信息,同时做一些很酷的事情(这通常意味着整个信息图在内存中可用)。
在某些情况下,您会看到将根目录保存到存储库中变得更加明确:
using transaction():
Root root = _repository.get(rootId)
root.doSomeCoolDomainThing(...)
_repository.save(root)
What is the root when you get all the way to the bottom?
根是一个域模型实体;它是"in charge",可以说,是那个特定聚合体中所有其他实体的那个。
在这里,我只是用它作为替代。在实际项目中,拼写会反映您所在领域的语言——Student、GradeBook、ReportCard 等
现在是加入派对的最佳时机,不用担心。您应该 不 直接从您的域模型访问数据持久性,而这应该在基础架构层中完成。如果您熟悉 Onion Architecture,它会很好地解释这一点。你可以查一下here or here。链接中描述的相同原则可以应用于您的案例。
访问持久层(通常称为基础设施)是通过执行您的用例,这些用例通常放置在应用层中并通过命令(操纵实体的状态)或查询(读取持久实体的状态)实现)
首先:存储库应该只引用聚合。在您的情况下,您的学生可以有多个成绩。
因此,您的存储库应该始终是聚合的持久性,而不是聚合内的实体。
这是我选择的解决方案:
域层聚合:
class Student
{
List<Grade> _grades = new List<Grade>();
int StudentId { get; set; }
public Student()
{
}
void AddGrade(int courseId, string grade)
{
var grade = new Grade(this.StudentId, courseId, grade);
_grades.Add(grade);
}
}
领域层的存储库接口:
interface IStudentRepository
{
void SaveStudent(int studentId);
// ...other methods
}
当然,正如上面帖子中提到的,Repository的实现应该发生在基础设施层。
您应该创建应用程序服务或 CommandHandler(如果您使用的是 CQRS)以集成域逻辑和基础结构:
class AddGradeService
{
private readonly IStudentRepository _studentRepository;
public AddGradeService(IStudentRepository studentRepository)
{
_studentRepository = studentRepository;
}
void Handle(int studentId, int courseId, string grade)
{
Student student = _studentRepository.Get(studentId);
student.AddGrade(courseId, grade);
_studentRepository.Save(student);
}
}
应该这样做:)
如果需要,您还可以参考我的示例 od DDD:https://github.com/czarek-szok/DDD.Ecommerce/
我正在研究领域驱动设计(是的,我来晚了),到目前为止我已经意识到领域模型应该是宇宙的中心。数据持久性只是一个实际的细节。
但是,如果这是真的,那么我很难弄清楚与持久性部分(例如存储库)的通信应该在哪里。
例如,如果我希望能够为学生添加新成绩,我是否应该像这样从学生域模型内部调用存储库?
interface IGradeRepository
{
void SaveGrade(int courseId, string grade);
// ...other methods
}
class Student
{
IGradeRepository _gradeRepository;
List<Grade> _grades = new List<Grade>();
public Student(IGradeRepository gradeRepository)
{
_gradeRepository = gradeRepository;
}
int StudentId { get; set; }
void AddGrade(int courseId, string grade)
{
var grade = new Grade(this.StudentId, courseId, grade);
_grades.Add(grade);
// Do I put the call to the data persistance here?
_gradeRepository.SaveGrade(grade);
}
}
如您所见,我最好的选择是从域模型内部访问存储库。但是,我不确定这是否是解决问题的方法。这似乎是许多教程中遗漏的内容之一。
总结一下:在进行领域驱动设计时,我从哪里访问数据层?
更新
正如某些人评论的那样,从我的示例中并不清楚我将如何访问学生 class。我可能会从视图模型或通过某种用例服务来做到这一点。
视图模型示例:
class StudentGradesViewModel
{
// (...) all sort of VM-stuff
private Student _student;
private Course _selectedCourse;
public void AddGrade(string grade)
{
_student.AddGrade(_course.CourseId, grade);
}
}
should I call the repository from inside the student domain model, like this?
这不是通常的模式。
相反,更常见的是与存储库的交互发生在应用层,而不是在域层。
存储库为应用程序提供了对聚合根的访问权限,并且应用程序仅与聚合根进行交互。所以简单的模式看起来像
using transaction():
Root root = _repository.get(rootId)
root.doSomeCoolDomainThing(...)
这里的假设是 root 可以访问维持其域不变所需的所有持久化信息,同时做一些很酷的事情(这通常意味着整个信息图在内存中可用)。
在某些情况下,您会看到将根目录保存到存储库中变得更加明确:
using transaction():
Root root = _repository.get(rootId)
root.doSomeCoolDomainThing(...)
_repository.save(root)
What is the root when you get all the way to the bottom?
根是一个域模型实体;它是"in charge",可以说,是那个特定聚合体中所有其他实体的那个。
在这里,我只是用它作为替代。在实际项目中,拼写会反映您所在领域的语言——Student、GradeBook、ReportCard 等
现在是加入派对的最佳时机,不用担心。您应该 不 直接从您的域模型访问数据持久性,而这应该在基础架构层中完成。如果您熟悉 Onion Architecture,它会很好地解释这一点。你可以查一下here or here。链接中描述的相同原则可以应用于您的案例。
访问持久层(通常称为基础设施)是通过执行您的用例,这些用例通常放置在应用层中并通过命令(操纵实体的状态)或查询(读取持久实体的状态)实现)
首先:存储库应该只引用聚合。在您的情况下,您的学生可以有多个成绩。 因此,您的存储库应该始终是聚合的持久性,而不是聚合内的实体。
这是我选择的解决方案:
域层聚合:
class Student
{
List<Grade> _grades = new List<Grade>();
int StudentId { get; set; }
public Student()
{
}
void AddGrade(int courseId, string grade)
{
var grade = new Grade(this.StudentId, courseId, grade);
_grades.Add(grade);
}
}
领域层的存储库接口:
interface IStudentRepository
{
void SaveStudent(int studentId);
// ...other methods
}
当然,正如上面帖子中提到的,Repository的实现应该发生在基础设施层。
您应该创建应用程序服务或 CommandHandler(如果您使用的是 CQRS)以集成域逻辑和基础结构:
class AddGradeService
{
private readonly IStudentRepository _studentRepository;
public AddGradeService(IStudentRepository studentRepository)
{
_studentRepository = studentRepository;
}
void Handle(int studentId, int courseId, string grade)
{
Student student = _studentRepository.Get(studentId);
student.AddGrade(courseId, grade);
_studentRepository.Save(student);
}
}
应该这样做:) 如果需要,您还可以参考我的示例 od DDD:https://github.com/czarek-szok/DDD.Ecommerce/