Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Technically DDD C# v10

pelshoff
October 01, 2019

Technically DDD C# v10

pelshoff

October 01, 2019
Tweet

More Decks by pelshoff

Other Decks in Programming

Transcript

  1. View Slide

  2. Technically
    DomainDrivenDesign
    #TechoramaNLNL @pelshoff

    View Slide

  3. View Slide

  4. View Slide

  5. Questions?

    View Slide

  6. Context
    Technical building blocks
    Quiz
    Case study I
    Case study II

    View Slide

  7. View Slide

  8. Context
    Technical building blocks
    Quiz
    Case study I
    Case study II

    View Slide

  9. Value objects

    View Slide

  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) {
    throw NameIsInvalid.BecausePartsAreMissing();
    }
    }
    private void LastNameIsRequired() {
    if (LastName.Length < 1) {
    throw NameIsInvalid.BecauseLastNameIsTooShort();
    } ...

    View Slide

  11. public class Email {
    public String EmailAsString { get; }
    public Email(String email) {
    EmailAsString = email;
    EmailMustBeAnActualEmailAddress();
    }
    private void EmailMustBeAnActualEmailAddress() {
    if (String.IsNullOrWhiteSpace(EmailAsString)) {
    throw EmailIsInvalid.BecauseEmailIsMissing();
    }
    if (!EmailAsString.Contains('@')) {
    throw EmailIsInvalid.BecauseAtIsMissing();
    }
    }
    }

    View Slide

  12. Value objects
    ● Represent a value
    ● Define allowed values
    ● Are immutable
    ● Are easy to test

    View Slide

  13. Entities

    View Slide

  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) {
    throw AttendeeIsInvalid.BecauseValuesAreNull();
    }
    }
    }

    View Slide

  15. Entities
    ● Have identity
    ● Are more than the sum of their
    attributes
    ● Evolve over time
    ● Are slightly harder to test

    View Slide

  16. Services

    View Slide

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

    View Slide

  18. Services
    ● Don’t have attributes or identity
    (Are not a “thing”)
    ● Are suitable for cross-concern
    cutting operations (involving
    multiple entities)
    ● Are harder to test

    View Slide

  19. Context
    Technical building blocks
    Quiz
    Case study I
    Case study II

    View Slide

  20. class Address

    View Slide

  21. class BicycleService

    View Slide

  22. class StreetAddress

    View Slide

  23. class Investment

    View Slide

  24. Context
    Technical building blocks
    Quiz
    Case study I
    Case study II

    View Slide

  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 program
    ) {
    // ...
    }
    }

    View Slide

  26. new Meeting(
    Guid.NewGuid(),
    "Techorama 2019",
    "...",
    "#TechoramaNL",
    "2019-10-01",
    "07:30",
    "2019-10-02",
    "17:30",
    true,
    "Deep Knowledge IT Conference",
    new List(){
    new ProgramSlot("2019-10-01", "07:30", "08:45", "Registration and Breakfast", "All of Pathé"),
    new ProgramSlot("2019-10-01", "17:45", "18:45", "Technically DDD", "Room 5"),
    }
    );

    View Slide

  27. Meetings cannot end before they start

    View Slide

  28. The Approach™
    1. Implement in Entity
    2. Extract Value Object
    3. Refactor Value Object

    View Slide

  29. public class Meeting {
    public Meeting(/* */) {
    // ...
    MeetingsCannotEndBeforeStart();
    }
    private void MeetingsCannotEndBeforeStart() {
    if (StartDate.CompareTo(EndDate) == -1) {
    return;
    }
    if (StartDate.CompareTo(EndDate) == 1) {
    throw MeetingIsInvalid.BecauseMeetingEndsBeforeStarting();
    }
    if (StartTime.CompareTo(EndTime) == 1) {
    throw MeetingIsInvalid.BecauseMeetingEndsBeforeStarting();
    }
    }
    }

    View Slide

  30. public class Meeting {
    // ...
    private MeetingDuration Duration;
    // ...
    public Meeting(
    Guid id,
    string title,
    string description,
    string code,
    MeetingDuration duration,
    bool isPublished,
    string subTitle,
    List program
    ) {
    // ...
    Duration = duration;
    // ...
    }
    }

    View Slide

  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) {
    throw MeetingIsInvalid.BecauseMeetingEndsBeforeStarting();
    }
    if (StartTime.CompareTo(EndTime) == 1) {
    throw MeetingIsInvalid.BecauseMeetingEndsBeforeStarting();
    }
    }
    }

    View Slide

  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) {
    throw MeetingIsInvalid.BecauseMeetingEndsBeforeStarting();
    }
    }
    }

    View Slide

  33. new Meeting(
    Guid.NewGuid(),
    "Techorama 2019",
    "...",
    "#TechoramaNL",
    new MeetingDuration(
    new DateTime(2019, 10, 1, 7, 30, 0),
    new DateTime(2019, 10, 2, 17, 30, 0)
    ),
    true,
    "Deep Knowledge IT Conference",
    new List(){
    new ProgramSlot("2019-10-01", "07:30", "08:45", "Registration and Breakfast", "All of Pathé"),
    new ProgramSlot("2019-10-01", "17:45", "18:45", "Technically DDD", "Room 5"),
    }
    );

    View Slide

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

    View Slide

  35. Program slots cannot occur in the same
    room at the same time

    View Slide

  36. new Meeting(
    Guid.NewGuid(),
    "Techorama 2019",
    "...",
    "#TechoramaNL",
    new MeetingDuration(
    new DateTime(2019, 10, 1, 7, 30, 0),
    new DateTime(2019, 10, 2, 17, 30, 0)
    ),
    true,
    "Deep Knowledge IT Conference",
    new List(){
    new ProgramSlot("2019-10-01", "07:30", "08:45", "Registration and Breakfast", "All of Pathé"),
    new ProgramSlot("2019-10-01", "17:45", "18:45", "Technically DDD", "Room 5"),
    }
    );

    View Slide

  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;
    }
    throw MeetingIsInvalid.BecauseProgramSlotsOverlap();
    }
    } // ...

    View Slide

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

    View Slide

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

    View Slide

  40. public class Program {
    private List Slots;
    public Program(List 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)) {
    throw MeetingIsInvalid.BecauseProgramSlotsOverlap();
    }
    }
    }
    }
    }

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  44. 1. 3.
    2. 4.
    This slot
    That slot
    This slot
    That slot
    This slot
    That slot
    This slot
    That slot
    Time

    View Slide

  45. This slot That slot
    This slot
    That slot
    Time
    1.
    2.

    View Slide

  46. 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);
    }
    }

    View Slide

  47. 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;
    }
    }

    View Slide

  48. new ProgramSlot("2019-10-01", "17:45", "18:45", "Technically DDD", "Room 5")
    private bool IsBefore(MeetingDuration that) {
    return this.Start.CompareTo(that.End) == 1;
    }

    View Slide

  49. 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;
    }
    }

    View Slide

  50. new Meeting(
    Guid.NewGuid(),
    "Techorama 2019",
    "...",
    "#TechoramaNL",
    "2019-10-01",
    "07:30",
    "2019-10-02",
    "17:30",
    true,
    "Deep Knowledge IT Conference",
    new List(){
    new ProgramSlot("2019-10-01", "07:30", "08:45", "Registration and Breakfast", "All of Pathé"),
    new ProgramSlot("2019-10-01", "17:45", "18:45", "Technically DDD", "Room 5"),
    }
    );

    View Slide

  51. new Meeting(
    Guid.NewGuid(), "Techorama 2019", "...", "#TechoramaNL",
    new MeetingDuration(new DateTime(2019, 10, 1, 7, 30, 0), new DateTime(2019, 10, 2, 17, 30, 0)),
    True, "Deep Knowledge IT Conference",
    new Program(
    new List(){
    new ProgramSlot(
    new SlotDuration(new DateTime(2019, 10, 1, 7, 30, 0), new DateTime(2019, 10, 1, 8, 45, 0)),
    "Registration and Breakfast",
    "All of Pathé"
    ),
    new ProgramSlot(
    new SlotDuration(new DateTime(2019, 10, 1, 17, 45, 0), new DateTime(2019, 10, 1, 18, 45, 0)),
    "Technically DDD",
    "Room 5"
    ),
    }
    )
    );

    View Slide

  52. A meeting must be able to be rescheduled

    View Slide

  53. Would you like to
    know more?
    “Domain-Driven Design: Tackling
    Complexity in the Heart of Software”,
    Eric Evans
    “Domain-Driven Design Distilled”,
    Vaughn Vernon

    View Slide

  54. Pim Elshoff
    developer.procurios.com
    @pelshoff
    https://speakerdeck.com/pelshoff/technically-ddd-c-v10

    View Slide