Technically DDD C

B84af63b07f297643ab1fd943c9ac59c?s=47 pelshoff
January 26, 2019

Technically DDD C

B84af63b07f297643ab1fd943c9ac59c?s=128

pelshoff

January 26, 2019
Tweet

Transcript

  1. None
  2. Technisch gezien DomainDrivenDesign #dotNedSaturday @pelshoff

  3. None
  4. None
  5. Vragen?

  6. Context Technische bouwblokken Quiz Case study I Case study II

  7. None
  8. Context Technische bouwblokken Quiz Case study I Case study II

  9. Value objects

  10. public class Name { public String LastName { get; }

    public String FirstName { get; } public Name (String lastName, String firstName) { LastName = lastName; FirstName = firstName; NamePartsMayNotBeNull(); LastNameIsRequired(); } private void NamePartsMayNotBeNull() { if (null == LastName || null == FirstName) { NameIsInvalid.becausePartsAreMissing(); } } private void LastNameIsRequired() { if (LastName.Length < 1) { NameIsInvalid.becauseLastNameIsTooShort(); } ...
  11. public class Email { public String EmailAsString { get; }

    public Email(String email) { EmailAsString = email; EmailMustBeAnActualEmailAddress(); } private void EmailMustBeAnActualEmailAddress() { if (String.IsNullOrWhiteSpace(EmailAsString)) { EmailIsInvalid.becauseEmailIsMissing(); } if (!EmailAsString.Contains('@')) { EmailIsInvalid.becauseAtIsMissing(); } } }
  12. Value objects • Vertegenwoordigen een waarde • Definiëren toegestane waarden

    • Zijn immutable • Zijn eenvoudig te testen
  13. Entities

  14. public class Attendee { public Guid Id { get; }

    private Name _Name; public Name Name { get { return _Name; } set { _Name = value; ValuesMayNotBeNull(); } } private Email _Email; public Email Email { get { return _Email; } set { _Email = value; ValuesMayNotBeNull(); } } public Attendee(Guid id, Name name, Email email) { Id = id; _Name = name; _Email = email; ValuesMayNotBeNull(); } private void ValuesMayNotBeNull() { if (null == _Name || null == _Email) { AttendeeIsInvalid.BecauseValuesAreNull(); } } }
  15. Entities • Hebben identiteit • Zijn meer dan hun attributes

    • Evoluëren over tijd • Zijn lastiger om te testen
  16. Services

  17. public class Registration { private RegistrationRepository Repository; public Registration(RegistrationRepository repository)

    { Repository = repository; } public void RegisterNew(Attendee Attendee) { EmailAddressMustBeUnique(Attendee.Email); Repository.Add(Attendee); } private void EmailAddressMustBeUnique(Email Email) { try { Repository.FindByEmail(Email); } catch (AttendeeNotFound ex) { return; } throw RegistrationFailed.becauseEmailAddressIsNotUnique(); } }
  18. Services • Hebben geen identiteit of attributes (Zijn geen “ding”)

    • Zijn geschikt voor cross-concern operations • Zijn nog lastiger om te testen
  19. Context Technische bouwblokken Quiz Case study I Case study II

  20. class Address

  21. class BicycleService

  22. class StreetAddress

  23. class Investment

  24. Context Technische bouwblokken Quiz Case study I Case study II

  25. public class Meeting { public Meeting( Guid id, string title,

    string description, string code, string startDate, string startTime, string endDate, string endTime, bool isPublished, string subTitle, List<ProgramSlot> program ) { // ... } }
  26. new Meeting( Guid.NewGuid(), "DotNed Saturday 2019", "...", "#dotNedSaturday", "2019-01-26", "09:00",

    "2019-01-26", "18:00", true, "HÉT NEDERLANDSE .NET COMMUNITY EVENEMENT", new List<ProgramSlot>(){ new ProgramSlot("2019-01-26", "09:00", "09:30", "Ontvangst en koffie", ""), new ProgramSlot("2019-01-26", "13:30", "14:30", "Technisch Gezien DDD", "Seminarruimte 1"), } );
  27. Meetings mogen niet eindigen voor ze gestart zijn

  28. De Aanpak™ 1. Implementeren in Entity 2. Extract Value Object

    3. Refactor Value Object
  29. public class Meeting { public Meeting(/* */) { // ...

    MeetingsCannotEndBeforeStart(); } private void MeetingsCannotEndBeforeStart() { if (StartDate.CompareTo(EndDate) == -1) { return; } if (StartDate.CompareTo(EndDate) == 1) { MeetingIsInvalid.becauseMeetingEndsBeforeStarting(); } if (StartTime.CompareTo(EndTime) == 1) { MeetingIsInvalid.becauseMeetingEndsBeforeStarting(); } } }
  30. public class Meeting { // ... private MeetingDuration Duration; //

    ... public Meeting( Guid id, string title, string description, string code, MeetingDuration duration, bool isPublished, string subTitle, List<ProgramSlot> program ) { // ... Duration = duration; // ... } }
  31. public class MeetingDuration { // ... public MeetingDuration(string startDate, string

    startTime, string endDate, string endTime) { // ... MeetingsCannotEndBeforeStart(); } private void MeetingsCannotEndBeforeStart() { if (StartDate.CompareTo(EndDate) == -1) { return; } if (StartDate.CompareTo(EndDate) == 1) { MeetingIsInvalid.becauseMeetingEndsBeforeStarting(); } if (StartTime.CompareTo(EndTime) == 1) { MeetingIsInvalid.becauseMeetingEndsBeforeStarting(); } } }
  32. public class MeetingDuration { private DateTime Start; private DateTime End;

    public MeetingDuration(DateTime start, DateTime end) { Start = start; End = end; MeetingsCannotEndBeforeStart(); } private void MeetingsCannotEndBeforeStart() { if (Start.CompareTo(End) == 1) { MeetingIsInvalid.becauseMeetingEndsBeforeStarting(); } } }
  33. new Meeting( Guid.NewGuid(), "DotNed Saturday 2019", "...", "#dotNedSaturday", new MeetingDuration(

    new DateTime(2019, 01, 26 , 9, 0, 0), new DateTime(2019, 01, 26 , 18, 0, 0) ), true, "HÉT NEDERLANDSE .NET COMMUNITY EVENEMENT", new List<ProgramSlot>(){ new ProgramSlot("2019-01-26", "09:00", "09:30", "Ontvangst en koffie", ""), new ProgramSlot("2019-01-26", "13:30", "14:30", "Technisch Gezien DDD", "Seminarruimte 1"), } );
  34. Context Technische bouwblokken Quiz Case study I Case study II

  35. Programmaonderdelen mogen niet plaatsvinden op hetzelfde moment in dezelfde ruimte

  36. new Meeting( Guid.NewGuid(), "DotNed Saturday 2019", "...", "#dotNedSaturday", new MeetingDuration(

    new DateTime(2019, 01, 26 , 9, 0, 0), new DateTime(2019, 01, 26 , 18, 0, 0) ), true, "HÉT NEDERLANDSE .NET COMMUNITY EVENEMENT", new List<ProgramSlot>(){ new ProgramSlot("2019-01-26", "09:00", "09:30", "Ontvangst en koffie", ""), new ProgramSlot("2019-01-26", "13:30", "14:30", "Technisch Gezien DDD", "Seminarruimte 1"), } );
  37. private void ProgramSlotsCannotOccurInTheSameRoomAtTheSameTime() { foreach (var slotA in Program) {

    foreach (var slotB in Program) { if (slotA == slotB) { continue; } if (!slotA.Room.Equals(slotB.Room)) { continue; } if (!slotA.Date.Equals(slotB.Date)) { continue; } if (slotA.StartTime.CompareTo(slotB.EndTime) == 1) { continue; } if (slotA.EndTime.CompareTo(slotB.StartTime) == -1) { continue; } MeetingIsInvalid.becauseProgramSlotsOverlap(); } } // ...
  38. public class Meeting { // ... private Program Program; public

    Meeting( Guid id, string title, string description, string code, MeetingDuration duration, bool isPublished, string subTitle, Program program ) { // ... Program = program; } }
  39. public class Program { private List<ProgramSlot> Slots; public Program(List<ProgramSlot> program)

    { Slots = program; ProgramSlotsCannotOccurInTheSameRoomAtTheSameTime(); } private void ProgramSlotsCannotOccurInTheSameRoomAtTheSameTime() { foreach (var slotA in Slots) { foreach (var slotB in Slots) { if (slotA == slotB) { continue; } // ... } } }
  40. public class Program { private List<ProgramSlot> Slots; public Program(List<ProgramSlot> program)

    { Slots = program; ProgramSlotsCannotOccurInTheSameRoomAtTheSameTime(); } private void ProgramSlotsCannotOccurInTheSameRoomAtTheSameTime() { foreach (var slotA in Slots) { foreach (var slotB in Slots) { if (slotA == slotB) { continue; } if (slotA.OverlapsWith(slotB)) { MeetingIsInvalid.becauseProgramSlotsOverlap(); } } } } }
  41. public class ProgramSlot { public MeetingDuration Duration; public string Title;

    public string Room; public ProgramSlot(MeetingDuration duration, string title, string room) { Duration = duration; Title = title; Room = room; } public bool OverlapsWith(ProgramSlot that) { return this.Room == that.Room && this.Duration.OverlapsWith(that.Duration); } }
  42. public class MeetingDuration { private DateTime Start; private DateTime End;

    public MeetingDuration(DateTime start, DateTime end) { Start = start; End = end; MeetingsCannotEndBeforeStart(); } private void MeetingsCannotEndBeforeStart() { /*...*/ } public bool OverlapsWith(MeetingDuration that) { return this.Start.CompareTo(that.Start) >= 0 && this.Start.CompareTo(that.End) < 1 || this.End.CompareTo(that.Start) >= 0 && this.End.CompareTo(that.End) < 1; } }
  43. public class MeetingDuration { private DateTime Start; private DateTime End;

    public MeetingDuration(DateTime start, DateTime end) { Start = start; End = end; MeetingsCannotEndBeforeStart(); } private void MeetingsCannotEndBeforeStart() { /*...*/ } public bool OverlapsWith(MeetingDuration that) { return this.Contains(that.Start) || this.Contains(that.End); } private bool Contains(DateTime Date) { return this.Start.CompareTo(Date) < 1 && this.End.CompareTo(Date) >= 0; } }
  44. public class MeetingDuration { private DateTime Start; private DateTime End;

    public MeetingDuration(DateTime start, DateTime end) { Start = start; End = end; MeetingsCannotEndBeforeStart(); } private void MeetingsCannotEndBeforeStart() { /*...*/ } public bool OverlapsWith(MeetingDuration that) { return !(this.Start.CompareTo(that.End) == 1 || this.End.CompareTo(that.Start) == -1); } }
  45. public class MeetingDuration { private DateTime Start; private DateTime End;

    public MeetingDuration(DateTime start, DateTime end) { Start = start; End = end; MeetingsCannotEndBeforeStart(); } private void MeetingsCannotEndBeforeStart() { /*...*/ } public bool OverlapsWith(MeetingDuration that) { return !this.IsBefore(that) && !that.IsBefore(this); } private bool IsBefore(MeetingDuration that) { return this.Start.CompareTo(that.End) == 1; } }
  46. new ProgramSlot( "2019-01-26", "13:30", "14:30", "Technisch Gezien DDD", "Seminarruimte 1"

    ) private bool IsBefore(MeetingDuration that) { return this.Start.CompareTo(that.End) == 1; }
  47. public class SlotDuration { private DateTime Start; private DateTime End;

    public SlotDuration(DateTime start, DateTime end) { Start = start; End = end; MeetingsCannotEndBeforeStart(); SlotMustStartAndEndOnSameDay(); } /*...*/ public bool OverlapsWith(SlotDuration that) { return !this.IsBefore(that) && !that.IsBefore(this); } private bool IsBefore(SlotDuration that) { return this.Start.CompareTo(that.End) >= 0; } }
  48. new Meeting( Guid.NewGuid(), "DotNed Saturday 2019", "...", "#dotNedSaturday", "2019-01-26", "09:00",

    "2019-01-26", "18:00", true, "HÉT NEDERLANDSE .NET COMMUNITY EVENEMENT", new List<ProgramSlot>(){ new ProgramSlot("2019-01-26", "09:00", "09:30", "Ontvangst en koffie", ""), new ProgramSlot("2019-01-26", "13:30", "14:30", "Technisch Gezien DDD", "Seminarruimte 1"), } );
  49. new Meeting( Guid.NewGuid(), "DotNed Saturday 2019", "...", "#dotNedSaturday", new MeetingDuration(new

    DateTime(2019, 01, 26 , 9, 0, 0), new DateTime(2019, 01, 26 , 18, 0, 0)), True, "HÉT NEDERLANDSE .NET COMMUNITY EVENEMENT", new Program( new List<ProgramSlot>(){ new ProgramSlot( new SlotDuration(new DateTime(2019, 1, 26, 9, 0, 0), new DateTime(2019, 1, 26, 9, 30, 0)), "Ontvangst en koffie", "" ), new ProgramSlot( new SlotDuration(new DateTime(2019, 1, 26, 13, 30, 0), new DateTime(2019, 1, 26, 14, 30, 0)), "Technisch Gezien DDD", "Seminarruimte 1" ), } ) );
  50. Een meeting moet herpland kunnen worden

  51. Meer weten? “Domain-Driven Design: Tackling Complexity in the Heart of

    Software”, Eric Evans “Domain-Driven Design Distilled”, Vaughn Vernon https://github.com/procurios/diconverg ent
  52. Pim Elshoff developer.procurios.com @pelshoff https://speakerdeck.com/pelshoff/technically-ddd-c

  53. None