DDD approach where to enforce business rules without aggregate?












-1















New to DDD I have a simple case a I would like to model using DDD approach



2 entities Student and Course



Relevant property for Student are StudentId and Budget



Relevant property for Course are CourseId and 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)



2) Student can book the course only if the user's budget is higher or equal to the course price.



3) Changes of course price doesn’t affect the students have already booked the course.



4) When the student book the course the his budget remains unchanged (maybe changes later at the end of the course)



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.

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)?



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. 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



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");
}
}
}
}









share|improve this question




















  • 1





    what have you tried? your question looks like homework

    – Adam Siemion
    Jan 2 at 22:29











  • I'm approaching DDD and I'm imagining in a hypothetical case like the one described where to define two simple business rules (the first probably a simple validation rule, the other an invariant to be respected whenever you change the value of Student.Budget

    – Ghini Antonio
    Jan 2 at 23:47













  • Setting a lower amount should throw a runtime error. That's the issue #1. I will follow up with the answer later, but for now just want to point that SetAmount is a "Crud" based thinking. You student should have Deposit and Withdraw methods. Not SetAmount. The actual 'SetAmount' logic would be incapsulated inside those methods, based on invariants.

    – DmitriBodiu
    Feb 5 at 7:31
















-1















New to DDD I have a simple case a I would like to model using DDD approach



2 entities Student and Course



Relevant property for Student are StudentId and Budget



Relevant property for Course are CourseId and 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)



2) Student can book the course only if the user's budget is higher or equal to the course price.



3) Changes of course price doesn’t affect the students have already booked the course.



4) When the student book the course the his budget remains unchanged (maybe changes later at the end of the course)



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.

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)?



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. 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



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");
}
}
}
}









share|improve this question




















  • 1





    what have you tried? your question looks like homework

    – Adam Siemion
    Jan 2 at 22:29











  • I'm approaching DDD and I'm imagining in a hypothetical case like the one described where to define two simple business rules (the first probably a simple validation rule, the other an invariant to be respected whenever you change the value of Student.Budget

    – Ghini Antonio
    Jan 2 at 23:47













  • Setting a lower amount should throw a runtime error. That's the issue #1. I will follow up with the answer later, but for now just want to point that SetAmount is a "Crud" based thinking. You student should have Deposit and Withdraw methods. Not SetAmount. The actual 'SetAmount' logic would be incapsulated inside those methods, based on invariants.

    – DmitriBodiu
    Feb 5 at 7:31














-1












-1








-1


1






New to DDD I have a simple case a I would like to model using DDD approach



2 entities Student and Course



Relevant property for Student are StudentId and Budget



Relevant property for Course are CourseId and 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)



2) Student can book the course only if the user's budget is higher or equal to the course price.



3) Changes of course price doesn’t affect the students have already booked the course.



4) When the student book the course the his budget remains unchanged (maybe changes later at the end of the course)



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.

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)?



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. 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



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");
}
}
}
}









share|improve this question
















New to DDD I have a simple case a I would like to model using DDD approach



2 entities Student and Course



Relevant property for Student are StudentId and Budget



Relevant property for Course are CourseId and 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)



2) Student can book the course only if the user's budget is higher or equal to the course price.



3) Changes of course price doesn’t affect the students have already booked the course.



4) When the student book the course the his budget remains unchanged (maybe changes later at the end of the course)



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.

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)?



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. 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



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");
}
}
}
}






oop design-patterns architecture domain-driven-design






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Jan 3 at 0:08







Ghini Antonio

















asked Jan 2 at 22:25









Ghini AntonioGhini Antonio

1,5161628




1,5161628








  • 1





    what have you tried? your question looks like homework

    – Adam Siemion
    Jan 2 at 22:29











  • I'm approaching DDD and I'm imagining in a hypothetical case like the one described where to define two simple business rules (the first probably a simple validation rule, the other an invariant to be respected whenever you change the value of Student.Budget

    – Ghini Antonio
    Jan 2 at 23:47













  • Setting a lower amount should throw a runtime error. That's the issue #1. I will follow up with the answer later, but for now just want to point that SetAmount is a "Crud" based thinking. You student should have Deposit and Withdraw methods. Not SetAmount. The actual 'SetAmount' logic would be incapsulated inside those methods, based on invariants.

    – DmitriBodiu
    Feb 5 at 7:31














  • 1





    what have you tried? your question looks like homework

    – Adam Siemion
    Jan 2 at 22:29











  • I'm approaching DDD and I'm imagining in a hypothetical case like the one described where to define two simple business rules (the first probably a simple validation rule, the other an invariant to be respected whenever you change the value of Student.Budget

    – Ghini Antonio
    Jan 2 at 23:47













  • Setting a lower amount should throw a runtime error. That's the issue #1. I will follow up with the answer later, but for now just want to point that SetAmount is a "Crud" based thinking. You student should have Deposit and Withdraw methods. Not SetAmount. The actual 'SetAmount' logic would be incapsulated inside those methods, based on invariants.

    – DmitriBodiu
    Feb 5 at 7:31








1




1





what have you tried? your question looks like homework

– Adam Siemion
Jan 2 at 22:29





what have you tried? your question looks like homework

– Adam Siemion
Jan 2 at 22:29













I'm approaching DDD and I'm imagining in a hypothetical case like the one described where to define two simple business rules (the first probably a simple validation rule, the other an invariant to be respected whenever you change the value of Student.Budget

– Ghini Antonio
Jan 2 at 23:47







I'm approaching DDD and I'm imagining in a hypothetical case like the one described where to define two simple business rules (the first probably a simple validation rule, the other an invariant to be respected whenever you change the value of Student.Budget

– Ghini Antonio
Jan 2 at 23:47















Setting a lower amount should throw a runtime error. That's the issue #1. I will follow up with the answer later, but for now just want to point that SetAmount is a "Crud" based thinking. You student should have Deposit and Withdraw methods. Not SetAmount. The actual 'SetAmount' logic would be incapsulated inside those methods, based on invariants.

– DmitriBodiu
Feb 5 at 7:31





Setting a lower amount should throw a runtime error. That's the issue #1. I will follow up with the answer later, but for now just want to point that SetAmount is a "Crud" based thinking. You student should have Deposit and Withdraw methods. Not SetAmount. The actual 'SetAmount' logic would be incapsulated inside those methods, based on invariants.

– DmitriBodiu
Feb 5 at 7:31












3 Answers
3






active

oldest

votes


















1














Point 5 is another validation rule. 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.






share|improve this answer
























  • It isn't an invariant. Invariant is regarding just one aggregate. You have two aggregates. - Invariants can also be between multiple aggregates, i.e make sure customer with the same code is unique in the system.

    – DmitriBodiu
    Feb 5 at 7:24











  • @DmitriBodiu "customer with the same code is unique in the system" just affects to the Customer aggregate. From what I know, an invariant is a business rule concerning to the state of an aggregate type, a rule that must be transactionally consistent. It just affects to an aggregate type, which defines a transactional boundary. See pages 205 and 353 of the book "Implementing domain driven design"

    – choquero70
    Feb 5 at 7:54











  • "customer with the same code is unique in the system" just affects to the Customer aggregate. No way. Its an invariant affecting all customers in the system. Unless you create a huge Company aggregate with Customers, then it becomes an invariant inside Company aggregate. (enterprisecraftsmanship.com/2016/09/22/…) is a great article explaining across-aggreagte invariant.

    – DmitriBodiu
    Feb 5 at 7:58











  • @DmitriBodiu I mean it just affects to Customer aggregate type. Anyway I do uniqueness checking in the repository of the aggregate, when you try to persist a customer you check that there's no other customer already stored having the same code. No other aggregate type is checked

    – choquero70
    Feb 5 at 16:44



















2














Hypothetical scenarios are typically tricky to comment on but I'll add my ZAR0.02 :)



You will have both a Student and a Course aggregate root. 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.



To enforce certain rules that cannot overlap it may be simpler to have a state regarding the budget on the Student. For instance, if the course is not in the BudgetApproved state then you cannot add to course. 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.






share|improve this answer































    0














    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. What you should do here is very simple: StudentService.SubscribeTo() and CourceService.Enroll(). This 2 methods should happen in 2 diffent transactions. First, inside StudentService.SubscribeTo you get student and course models from persistence, then you call 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).






    share|improve this answer
























      Your Answer






      StackExchange.ifUsing("editor", function () {
      StackExchange.using("externalEditor", function () {
      StackExchange.using("snippets", function () {
      StackExchange.snippets.init();
      });
      });
      }, "code-snippets");

      StackExchange.ready(function() {
      var channelOptions = {
      tags: "".split(" "),
      id: "1"
      };
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function() {
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled) {
      StackExchange.using("snippets", function() {
      createEditor();
      });
      }
      else {
      createEditor();
      }
      });

      function createEditor() {
      StackExchange.prepareEditor({
      heartbeatType: 'answer',
      autoActivateHeartbeat: false,
      convertImagesToLinks: true,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: 10,
      bindNavPrevention: true,
      postfix: "",
      imageUploader: {
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      },
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      });


      }
      });














      draft saved

      draft discarded


















      StackExchange.ready(
      function () {
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54013963%2fddd-approach-where-to-enforce-business-rules-without-aggregate%23new-answer', 'question_page');
      }
      );

      Post as a guest















      Required, but never shown

























      3 Answers
      3






      active

      oldest

      votes








      3 Answers
      3






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes









      1














      Point 5 is another validation rule. 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.






      share|improve this answer
























      • It isn't an invariant. Invariant is regarding just one aggregate. You have two aggregates. - Invariants can also be between multiple aggregates, i.e make sure customer with the same code is unique in the system.

        – DmitriBodiu
        Feb 5 at 7:24











      • @DmitriBodiu "customer with the same code is unique in the system" just affects to the Customer aggregate. From what I know, an invariant is a business rule concerning to the state of an aggregate type, a rule that must be transactionally consistent. It just affects to an aggregate type, which defines a transactional boundary. See pages 205 and 353 of the book "Implementing domain driven design"

        – choquero70
        Feb 5 at 7:54











      • "customer with the same code is unique in the system" just affects to the Customer aggregate. No way. Its an invariant affecting all customers in the system. Unless you create a huge Company aggregate with Customers, then it becomes an invariant inside Company aggregate. (enterprisecraftsmanship.com/2016/09/22/…) is a great article explaining across-aggreagte invariant.

        – DmitriBodiu
        Feb 5 at 7:58











      • @DmitriBodiu I mean it just affects to Customer aggregate type. Anyway I do uniqueness checking in the repository of the aggregate, when you try to persist a customer you check that there's no other customer already stored having the same code. No other aggregate type is checked

        – choquero70
        Feb 5 at 16:44
















      1














      Point 5 is another validation rule. 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.






      share|improve this answer
























      • It isn't an invariant. Invariant is regarding just one aggregate. You have two aggregates. - Invariants can also be between multiple aggregates, i.e make sure customer with the same code is unique in the system.

        – DmitriBodiu
        Feb 5 at 7:24











      • @DmitriBodiu "customer with the same code is unique in the system" just affects to the Customer aggregate. From what I know, an invariant is a business rule concerning to the state of an aggregate type, a rule that must be transactionally consistent. It just affects to an aggregate type, which defines a transactional boundary. See pages 205 and 353 of the book "Implementing domain driven design"

        – choquero70
        Feb 5 at 7:54











      • "customer with the same code is unique in the system" just affects to the Customer aggregate. No way. Its an invariant affecting all customers in the system. Unless you create a huge Company aggregate with Customers, then it becomes an invariant inside Company aggregate. (enterprisecraftsmanship.com/2016/09/22/…) is a great article explaining across-aggreagte invariant.

        – DmitriBodiu
        Feb 5 at 7:58











      • @DmitriBodiu I mean it just affects to Customer aggregate type. Anyway I do uniqueness checking in the repository of the aggregate, when you try to persist a customer you check that there's no other customer already stored having the same code. No other aggregate type is checked

        – choquero70
        Feb 5 at 16:44














      1












      1








      1







      Point 5 is another validation rule. 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.






      share|improve this answer













      Point 5 is another validation rule. 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.







      share|improve this answer












      share|improve this answer



      share|improve this answer










      answered Jan 3 at 8:10









      choquero70choquero70

      1,45421732




      1,45421732













      • It isn't an invariant. Invariant is regarding just one aggregate. You have two aggregates. - Invariants can also be between multiple aggregates, i.e make sure customer with the same code is unique in the system.

        – DmitriBodiu
        Feb 5 at 7:24











      • @DmitriBodiu "customer with the same code is unique in the system" just affects to the Customer aggregate. From what I know, an invariant is a business rule concerning to the state of an aggregate type, a rule that must be transactionally consistent. It just affects to an aggregate type, which defines a transactional boundary. See pages 205 and 353 of the book "Implementing domain driven design"

        – choquero70
        Feb 5 at 7:54











      • "customer with the same code is unique in the system" just affects to the Customer aggregate. No way. Its an invariant affecting all customers in the system. Unless you create a huge Company aggregate with Customers, then it becomes an invariant inside Company aggregate. (enterprisecraftsmanship.com/2016/09/22/…) is a great article explaining across-aggreagte invariant.

        – DmitriBodiu
        Feb 5 at 7:58











      • @DmitriBodiu I mean it just affects to Customer aggregate type. Anyway I do uniqueness checking in the repository of the aggregate, when you try to persist a customer you check that there's no other customer already stored having the same code. No other aggregate type is checked

        – choquero70
        Feb 5 at 16:44



















      • It isn't an invariant. Invariant is regarding just one aggregate. You have two aggregates. - Invariants can also be between multiple aggregates, i.e make sure customer with the same code is unique in the system.

        – DmitriBodiu
        Feb 5 at 7:24











      • @DmitriBodiu "customer with the same code is unique in the system" just affects to the Customer aggregate. From what I know, an invariant is a business rule concerning to the state of an aggregate type, a rule that must be transactionally consistent. It just affects to an aggregate type, which defines a transactional boundary. See pages 205 and 353 of the book "Implementing domain driven design"

        – choquero70
        Feb 5 at 7:54











      • "customer with the same code is unique in the system" just affects to the Customer aggregate. No way. Its an invariant affecting all customers in the system. Unless you create a huge Company aggregate with Customers, then it becomes an invariant inside Company aggregate. (enterprisecraftsmanship.com/2016/09/22/…) is a great article explaining across-aggreagte invariant.

        – DmitriBodiu
        Feb 5 at 7:58











      • @DmitriBodiu I mean it just affects to Customer aggregate type. Anyway I do uniqueness checking in the repository of the aggregate, when you try to persist a customer you check that there's no other customer already stored having the same code. No other aggregate type is checked

        – choquero70
        Feb 5 at 16:44

















      It isn't an invariant. Invariant is regarding just one aggregate. You have two aggregates. - Invariants can also be between multiple aggregates, i.e make sure customer with the same code is unique in the system.

      – DmitriBodiu
      Feb 5 at 7:24





      It isn't an invariant. Invariant is regarding just one aggregate. You have two aggregates. - Invariants can also be between multiple aggregates, i.e make sure customer with the same code is unique in the system.

      – DmitriBodiu
      Feb 5 at 7:24













      @DmitriBodiu "customer with the same code is unique in the system" just affects to the Customer aggregate. From what I know, an invariant is a business rule concerning to the state of an aggregate type, a rule that must be transactionally consistent. It just affects to an aggregate type, which defines a transactional boundary. See pages 205 and 353 of the book "Implementing domain driven design"

      – choquero70
      Feb 5 at 7:54





      @DmitriBodiu "customer with the same code is unique in the system" just affects to the Customer aggregate. From what I know, an invariant is a business rule concerning to the state of an aggregate type, a rule that must be transactionally consistent. It just affects to an aggregate type, which defines a transactional boundary. See pages 205 and 353 of the book "Implementing domain driven design"

      – choquero70
      Feb 5 at 7:54













      "customer with the same code is unique in the system" just affects to the Customer aggregate. No way. Its an invariant affecting all customers in the system. Unless you create a huge Company aggregate with Customers, then it becomes an invariant inside Company aggregate. (enterprisecraftsmanship.com/2016/09/22/…) is a great article explaining across-aggreagte invariant.

      – DmitriBodiu
      Feb 5 at 7:58





      "customer with the same code is unique in the system" just affects to the Customer aggregate. No way. Its an invariant affecting all customers in the system. Unless you create a huge Company aggregate with Customers, then it becomes an invariant inside Company aggregate. (enterprisecraftsmanship.com/2016/09/22/…) is a great article explaining across-aggreagte invariant.

      – DmitriBodiu
      Feb 5 at 7:58













      @DmitriBodiu I mean it just affects to Customer aggregate type. Anyway I do uniqueness checking in the repository of the aggregate, when you try to persist a customer you check that there's no other customer already stored having the same code. No other aggregate type is checked

      – choquero70
      Feb 5 at 16:44





      @DmitriBodiu I mean it just affects to Customer aggregate type. Anyway I do uniqueness checking in the repository of the aggregate, when you try to persist a customer you check that there's no other customer already stored having the same code. No other aggregate type is checked

      – choquero70
      Feb 5 at 16:44













      2














      Hypothetical scenarios are typically tricky to comment on but I'll add my ZAR0.02 :)



      You will have both a Student and a Course aggregate root. 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.



      To enforce certain rules that cannot overlap it may be simpler to have a state regarding the budget on the Student. For instance, if the course is not in the BudgetApproved state then you cannot add to course. 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.






      share|improve this answer




























        2














        Hypothetical scenarios are typically tricky to comment on but I'll add my ZAR0.02 :)



        You will have both a Student and a Course aggregate root. 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.



        To enforce certain rules that cannot overlap it may be simpler to have a state regarding the budget on the Student. For instance, if the course is not in the BudgetApproved state then you cannot add to course. 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.






        share|improve this answer


























          2












          2








          2







          Hypothetical scenarios are typically tricky to comment on but I'll add my ZAR0.02 :)



          You will have both a Student and a Course aggregate root. 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.



          To enforce certain rules that cannot overlap it may be simpler to have a state regarding the budget on the Student. For instance, if the course is not in the BudgetApproved state then you cannot add to course. 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.






          share|improve this answer













          Hypothetical scenarios are typically tricky to comment on but I'll add my ZAR0.02 :)



          You will have both a Student and a Course aggregate root. 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.



          To enforce certain rules that cannot overlap it may be simpler to have a state regarding the budget on the Student. For instance, if the course is not in the BudgetApproved state then you cannot add to course. 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.







          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Jan 3 at 4:48









          Eben RouxEben Roux

          9,69921538




          9,69921538























              0














              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. What you should do here is very simple: StudentService.SubscribeTo() and CourceService.Enroll(). This 2 methods should happen in 2 diffent transactions. First, inside StudentService.SubscribeTo you get student and course models from persistence, then you call 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).






              share|improve this answer




























                0














                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. What you should do here is very simple: StudentService.SubscribeTo() and CourceService.Enroll(). This 2 methods should happen in 2 diffent transactions. First, inside StudentService.SubscribeTo you get student and course models from persistence, then you call 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).






                share|improve this answer


























                  0












                  0








                  0







                  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. What you should do here is very simple: StudentService.SubscribeTo() and CourceService.Enroll(). This 2 methods should happen in 2 diffent transactions. First, inside StudentService.SubscribeTo you get student and course models from persistence, then you call 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).






                  share|improve this answer













                  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. What you should do here is very simple: StudentService.SubscribeTo() and CourceService.Enroll(). This 2 methods should happen in 2 diffent transactions. First, inside StudentService.SubscribeTo you get student and course models from persistence, then you call 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).







                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered Feb 5 at 8:04









                  DmitriBodiuDmitriBodiu

                  13611




                  13611






























                      draft saved

                      draft discarded




















































                      Thanks for contributing an answer to Stack Overflow!


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid



                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.


                      To learn more, see our tips on writing great answers.




                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function () {
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54013963%2fddd-approach-where-to-enforce-business-rules-without-aggregate%23new-answer', 'question_page');
                      }
                      );

                      Post as a guest















                      Required, but never shown





















































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown

































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown







                      Popular posts from this blog

                      MongoDB - Not Authorized To Execute Command

                      in spring boot 2.1 many test slices are not allowed anymore due to multiple @BootstrapWith

                      How to fix TextFormField cause rebuild widget in Flutter