From 89a1158c93377c5afa2b3d3f8120ead5c39d82c2 Mon Sep 17 00:00:00 2001 From: Willem van den Ende Date: Wed, 16 Sep 2026 22:32:35 +0100 Subject: [PATCH] Exercise 1 solution: DiscountedTotalInCents on the domain order + integration tests Domain-only field: OrderDto and OrderData stay without it; the stored row keeps the original total after saving a discounted order. The discount percentage is not copied (derivable). yak: exercise-1-solution-on-branch-exercise-1-solution-4s5q --- src/After/Order.cs | 9 +++ .../DiscountedOrderMappingTests.cs | 59 +++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 tests/BeforeAfter.Tests/DiscountedOrderMappingTests.cs diff --git a/src/After/Order.cs b/src/After/Order.cs index 9892c2e..95777a9 100644 --- a/src/After/Order.cs +++ b/src/After/Order.cs @@ -24,6 +24,15 @@ public class Order /// public int TotalInCents { get; set; } + /// + /// The discounted total in cents, copied onto the order by the caller of + /// the discount flow (see ). Derived + /// data: it lives in the domain only — neither + /// nor carries it — and the applied percentage + /// is not stored either, being derivable from the two totals. + /// + public int DiscountedTotalInCents { get; set; } + /// /// The client that owns this order. Set automatically by /// when the order is added to diff --git a/tests/BeforeAfter.Tests/DiscountedOrderMappingTests.cs b/tests/BeforeAfter.Tests/DiscountedOrderMappingTests.cs new file mode 100644 index 0000000..0c1cb6a --- /dev/null +++ b/tests/BeforeAfter.Tests/DiscountedOrderMappingTests.cs @@ -0,0 +1,59 @@ +using After; +using static BeforeAfter.Tests.TestData.ClientBuilder; +using static BeforeAfter.Tests.TestData.OrderBuilder; + +namespace BeforeAfter.Tests; + +/// +/// EXERCISE — the mapping from a onto an +/// is NOT one-on-one. (This is the solution participants +/// arrive at from the stub in EXERCISE.md.) +/// +public class DiscountedOrderMappingTests +{ + [Fact] // EXERCISE STEP 2 + public void A_discounted_order_can_be_correlated_onto_its_order() + { + var db = new DbContext(); + var order = ValidOrder().WithDescription("big order").WithTotal(150_00).Build(); + var client = ValidClient().WithName("Acme").WithOrder(order).Build(); + db.Save(client); + db.Save(order); + + // 10 % for orders above €100: + var discounted = DiscountCalculator.CalculateDiscount( + order, discountBarrierCents: 100_00, discountPercentage: 10); + + // Correlate: the discounted result belongs to the order carrying this id. + Assert.Equal(order.Id, discounted.OrderId); + + // Copy the discounted total onto the order. The discount percentage is + // deliberately NOT copied — it is derivable from the two totals. + order.DiscountedTotalInCents = discounted.DiscountedTotalInCents; + + Assert.Equal(135_00, order.DiscountedTotalInCents); + } + + [Fact] // EXERCISE STEP 3 + public void Saving_a_discounted_order_keeps_the_stored_total_untouched() + { + var db = new DbContext(); + var order = ValidOrder().WithDescription("big order").WithTotal(150_00).Build(); + var client = ValidClient().WithName("Acme").WithOrder(order).Build(); + db.Save(client); + db.Save(order); + + var discounted = DiscountCalculator.CalculateDiscount( + order, discountBarrierCents: 100_00, discountPercentage: 10); + Assert.Equal(order.Id, discounted.OrderId); + order.DiscountedTotalInCents = discounted.DiscountedTotalInCents; + + // Save the updated order: the context refreshes the stored row in place. + db.Save(order); + + // The persisted record keeps the ORIGINAL total: the database knows + // nothing about discounts — derived data lives in the domain only. + var row = db.Find(candidate => candidate.Id == discounted.OrderId)!; + Assert.Equal(150_00, row.TotalInCents); + } +}