简体   繁体   English

DDD方法在哪里执行业务规则而不进行汇总?

[英]DDD approach where to enforce business rules without aggregate?

New to DDD I have a simple case a I would like to model using DDD approach DDD的新手我有一个简单的案例,我想使用DDD方法建模

2 entities Student and Course 2个实体StudentCourse

Relevant property for Student are StudentId and Budget 学生的相关属性是StudentId和Budget

Relevant property for Course are CourseId and Price 课程的相关属性是CourseId和Price

Student and Course are entities that can exists on its own and have their own life cycle 学生和课程是可以独立存在并具有自己生命周期的实体

Business requirements: 业务需求:

1) Student can book one course (CourseId is fk for Student table) 1)学生可以预订一门课程(CourseId是学生表的fk)

2) Student can book the course only if the user's budget is higher or equal to the course price. 2)仅当用户的预算高于或等于课程价格时,学生才能预订课程。

3) Changes of course price doesn't affect the students have already booked the course. 3)课程价格的变化不会影响已经预订课程的学生。

4) When the student book the course the his budget remains unchanged (maybe changes later at the end of the course) 4)当学生预定课程时,他的预算保持不变(可能在课程结束时更改)

5) Student budget can be modified setting a different amount but new amount have to be higher or equal to the price of the course the user booked. 5)可以修改学生预算,设置不同的金额,但是新的金额必须高于或等于用户预订课程的价格。
Setting a lower amount should throw a runtime error. 设置较低的数量将引发运行时错误。

What the way to model this simple case following domain driven design? 在域驱动设计之后,如何以这种方式为这种简单案例建模? Where to enforce the two busines rules (points 2 and 5)? 在哪里执行这两个业务规则(第2点和第5点)?

As a Course can exist without a Student I can't define the aggregate where Student is the root entity and Course its child entity. 由于课程可以在没有学生的情况下存在,因此我无法定义汇总,其中学生是根实体,课程是其子实体。 Can I? 我可以吗?

But at the same time the business rule defined at point 5 seems to me be an invariants. 但是同时,在我看来,第5点定义的业务规则是不变的。 Is it? 是吗?

So where and how to apply this rules? 那么在哪里以及如何应用此规则?

I tried a service approach, can work for the first simple rule (point 2) but fail for the rule described at point 5 我尝试了一种服务方法,可以使用第一个简单规则(第2点),但不能使用第5点描述的规则

var student = studentRepository.Get(srtudentId);
var course = courseRepository.Get(courseId)

var studentService = new StudentService();

studentService.SubScribeStudentToCourse(student, course);

studentRepository.Update(student);


StudentService.ChangeStudentBudget(student, 100000);

studentRepository.Update(student);  

when I update the student with the new budget someone else can change the course price making the student budget inconsistent 当我用新的预算更新学生时,其他人可以更改课程价格,从而导致学生预算不一致

public class StudentService
{
    SubScribeStudentToCourse(Studen student, Course course)
    {
        if (studentt.Budget >= course.Price)
        {
            student.CourseId = course.CourseId
        }
    }

    ChangeStudentBudget( Student student, decimal budgetAmount)
    {
        if (student.CourseId != null)
        {
            var studentCourse = courseRepository.Get(student.CourseId);
            if ( studentCourse.Price <= budgetAmount)
            {
                student.Budget = budgetAmount;
            }
            else
            {
                throw new Exception("Budget should be higher than studentCourse.Price");
            }
        }
    }
}

Hypothetical scenarios are typically tricky to comment on but I'll add my ZAR0.02 :) 假设场景通常很难评论,但我将添加ZAR0.02 :)

You will have both a Student and a Course aggregate root. 您将同时拥有“ Student 和“ Course聚合根。 If you need the relationship to the other defined then store either a list of ids or a value object that represents the other side. 如果需要与定义的另一方的关系,则存储ID列表或代表另一方的值对象。

To enforce certain rules that cannot overlap it may be simpler to have a state regarding the budget on the Student . 要实施某些不能重叠的规则,对Student的预算进行陈述可能更简单。 For instance, if the course is not in the BudgetApproved state then you cannot add to course. 例如,如果课程未处于“ BudgetApproved状态,则无法添加到课程。 In order to change the budget you would first need to change the state to, say, 'Budgeting'. 为了更改预算,您首先需要将状态更改为“预算”。 In this way you introduce more distinct steps that allow better control of your invariants. 这样,您将引入更多不同的步骤,以更好地控制不变量。

Just another note on changing prices. 只是关于价格变动的另一个说明。 These things would probably work on a "quote" basis in any event. 无论如何,这些事情可能会在“引用”的基础上起作用。 Once you "Accept" the quote any changes in price are irrelevant unless there is an "Error" or "Omission" that could, and should, be dealt with using some business process or, if not defined in the system, out-of-band. 一旦“接受”报价,价格的任何变化都是不相关的,除非存在“错误”或“遗漏”,这些错误或“遗漏”可以并且应该使用某些业务流程来处理,或者,如果未在系统中定义,则超出带。 An Order may be Cancelled or 'Abandoned` and then some other process such as a reimbursement kicks in. Order可能被Cancelled或“放弃”,然后启动其他程序,例如报销。

Point 5 is another validation rule. 第5点是另一个验证规则。 But if course price can be modified, you will have to check the rule there too, not just when student budget is modified. 但是,如果可以修改课程价格,则不仅要修改学生预算,还必须在这里检查规则。

It isn't an invariant. 它不是不变的。 Invariant is regarding just one aggregate. 不变只涉及一个集合。 You have two aggregates. 您有两个聚合。

This question sounds to me like I already answered it. 这个问题对我来说听起来像我已经回答了。

Your aggregates must be eventually consistent, not strongly, unless it's a really really important scenario. 除非这是一个非常重要的场景,否则您的聚合最终必须是一致的,而不是强烈的。 If it is, then consider using Saga, or update them in one transaction. 如果是这样,则考虑使用Saga,或在一项交易中对其进行更新。 What you should do here is very simple: StudentService.SubscribeTo() and CourceService.Enroll(). 您在这里应该做的很简单:StudentService.SubscribeTo()和CourceService.Enroll()。 This 2 methods should happen in 2 diffent transactions. 这2种方法应在2个不同的事务中发生。 First, inside StudentService.SubscribeTo you get student and course models from persistence, then you call student.SubscribeTo(course). 首先,在StudentService.SubscribeTo内部,从持久性中获取学生和课程模型,然后调用student.SubscribeTo(course)。 After the operation, you raise student assignedToCourse Domain Event and StudentDomainEventsHandler catches it, and calls CourceService.Enroll() which gets student and course models from persistence, then calls course.Enroll(student). 操作完成后,您引发学生assignedToCourse域事件,然后StudentDomainEventsHandler捕获该事件,并调用CourceService.Enroll()从持久性中获取学生和课程模型,然后调用course.Enroll(student)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM