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

Domain Driven Design Kinda

Domain Driven Design Kinda

Explains how we kinda do Domain Driven Design in our Ruby on Rails at Product Hunt.

Radoslav Stankov

March 14, 2021
Tweet

More Decks by Radoslav Stankov

Other Decks in Technology

Transcript

  1. Domain Driven


    Desig
    n

    kinda

    View full-size slide

  2. Domain Driven
    Design

    View full-size slide

  3. ...kinda 🙈

    View full-size slide

  4. Product Hunt favored
    Domain Driven


    Desig
    n

    View full-size slide

  5. “Domain-driven design (DDD) is the concept
    that the structure and language of software
    code (classes, methods, functions, variables).”
    What is Domain Driven Design?

    View full-size slide

  6. What problems does DDD solve?
    “Developers are too wrapped up with
    technology and trying to solve problems using
    technology rather than careful thought and
    design”
    1

    View full-size slide

  7. What problems does DDD solve?
    “The database is given too much priority, and
    most discussions about the solutions center
    around the database and a data model rather
    than business processes and operations.”
    2

    View full-size slide

  8. What problems does DDD solve?
    “Developers don’t give proper emphasis to
    naming objects and operations according to
    the business purpose that they
    fi
    ll.”
    3

    View full-size slide

  9. What problems does DDD solve?
    “Application logic is disjointed and hard to
    follow. It is hard to see an overview of the
    application features”
    4

    View full-size slide

  10. What are the issues with traditional DDD?
    “In order to help maintain the model as a pure
    and helpful language construct, the team must
    typically implement a great deal of isolation and
    encapsulation within the domain model. Which
    overcomplicates the system.”
    1

    View full-size slide

  11. What are the issues with traditional DDD?
    “Rails and Node doesn't
    fi
    t very well with DDD
    mindset. DDD comes from Java and similar
    languages.”
    2

    View full-size slide

  12. What are the issues with traditional DDD?
    “A lot of DDD practices are focused on
    consultancy model and are used for model
    complicated business processes.”
    3

    View full-size slide

  13. Product Hunt favored
    Domain Driven


    Desig
    n

    View full-size slide

  14. What


    is


    Domain Driven Design
    ?

    View full-size slide

  15. Ubiquitous Language
    “A language structured around the domain
    model and used by all team members to
    connect all the activities of the team with the
    software.”

    View full-size slide

  16. Ubiquitous Language
    “There are only two hard things in Computer
    Science: cache invalidation and naming things.”


    -- Phil Karlton

    View full-size slide

  17. Ubiquitous Language
    app/models/use.rb 🙈

    View full-size slide

  18. Ubiquitous Language
    app/models/use.rb 🤮

    View full-size slide

  19. Ubiquitous Language
    app/models/stack_item.rb 👍

    View full-size slide

  20. Ubiquitous Language
    app/model/founder_club/member.rb 👍

    View full-size slide

  21. Domains
    “A sphere of knowledge, in
    fl
    uence, or activity.
    The subject area to which the user applies a
    program is the domain of the software;”

    View full-size slide

  22. Domain Types
    🎂 Core domain

    🍰 Supporting sub domain

    🧁 Generic sub domain

    View full-size slide

  23. Domain Types
    🎂 Core domain

    🍰 Supporting sub domain

    🧁 Generic sub domain

    🥮 Utilities (infrastructure) sub domain

    View full-size slide

  24. Domain Types
    🎂 Core domain - Post, User, Ship

    🍰 Supporting sub domain - Collections, Discussions

    🧁 Generic sub domain - Votes, Comments, Seo

    🥮 Utilities - External, ExecSQL, HandleRaceCondition

    View full-size slide

  25. What is PH Code Domain

    View full-size slide

  26. Moderation
    Voting Comments
    Posts
    Collections
    Maker Goals
    Sharing
    Seo
    Discussions
    Ask
    Ship
    Mentors Founder Club Golden Kitty
    Maker Fest
    Topics
    Stories
    Spam Newsletter
    Noti
    fi
    cations Metrics
    Users
    Payments
    Activity Feed
    Jobs

    View full-size slide

  27. Moderation
    Voting Comments
    Posts
    Collections
    Maker Goals
    Sharing
    Seo
    Discussions
    Ask
    Ship
    Mentors Founder Club Golden Kitty
    Maker Fest
    Topics
    Stories
    Spam Newsletter
    Noti
    fi
    cations Metrics
    Users
    Payments
    Activity Feed
    Jobs
    Core Feature Generic
    Context Map

    View full-size slide

  28. Moderation
    Voting Comments
    Posts
    Collections
    Maker Goals
    Sharing
    Seo
    Discussions
    Ask
    Ship
    Mentors Founder Club Golden Kitty
    Maker Fest
    Topics
    Stories
    Spam Newsletter
    Noti
    fi
    cations Metrics
    Users
    Payments
    Activity Feed
    Jobs
    Core Feature Generic
    Context Map
    Utilities

    View full-size slide

  29. Bounded Context
    “A semantic contextual boundary. This means
    that within the boundary each component of the
    software model has a speci
    fi
    c meaning and
    does speci
    fi
    c things.”

    View full-size slide

  30. ...kinda 🙈

    View full-size slide

  31. Admin
    Other


    Bounded
    Context
    Utilities


    Bounded Context (Domain)
    Models Services Jobs
    GraphQL
    Mutations Resolvers Types

    View full-size slide

  32. https://www.phoenixframework.org/

    View full-size slide

  33. defmodule Hello.Accounts do


    @moduledoc """


    The Accounts context.


    """


    import Ecto.Query, warn: false


    alias Hello.Repo


    alias Hello.Accounts.User


    @doc """


    Returns the list of users.


    ## Examples


    iex> list_users()


    [%User{}, ...]


    """


    def list_users do


    Repo.all(User)


    end


    ...


    end

    View full-size slide

  34. Showcase 1

    Feature Domain

    View full-size slide

  35. /app/domain/

    /app/domain/founder_club.r
    b

    /app/domain/founder_club/*.rb

    View full-size slide

  36. FounderClub.join_waitlist(user:)

    FounderClub.join(user:)


    FounderClub.request_access(user:, code:)


    FounderClub.deliver_waitlist_invite(invite:)


    FounderClub.plan_for(code:)

    FounderClub.subscribe(user:, subscription_id:)


    FounderClub.claim_deal(user:, deal:)


    FounderClub.claimed_deals_by(user:)


    FounderClub.deals_available_to(user:)


    FounderClub.create_referral_code(user:, email:)


    FounderClub.destroy_referral_code(user:, email:)

    FounderClub.referrals_of(user:)


    FounderClub.active_subscription?(user)


    FounderClub.list_subscriptions

    FounderClub.transfer_subscription(from:, to:)

    View full-size slide

  37. FounderClub::Admin::DealForm

    FounderClub::Policy

    FounderClub::MailerInfo
    🤷

    View full-size slide

  38. FounderClub.admin_deal_form

    FounderClub.policy

    FounderClub.mail_info
    🤔

    View full-size slide

  39. module FounderClub


    extend self



    def admin_deal_form

    FounderClub::Admin::DealForm

    end


    def policy

    FounderClub::Policy

    end


    def mailer_info


    FounderClub::MailerInfo

    end

    end
    🤔

    View full-size slide

  40. Showcase 2

    Generic Domain

    View full-size slide

  41. Sharing.text_for(post)

    Sharing.text_for(post, current_user: user)

    Sharing.image_for(post)

    Sharing.image_for_comment(comment, highlighted_text: 'Awesome')

    View full-size slide

  42. module Sharing


    extend self


    def text_for(subject, current_user: nil)


    generator = FindConst.call(Sharing::Text, subject)


    if generator.method(:call).arity == 1


    generator.call(subject)


    else


    generator.call(subject, current_user: current_user)


    end


    end


    def image_for(subject)


    generator = FindConst.call(Sharing::ImageUrl, subject)


    generator.call(subject)


    end


    def image_for_comment(comment, highlighted_text:)


    Sharing::ImageUrl:Comment.call(comment, highlighted_text: highlighted_text)


    end


    endd


    View full-size slide

  43. Domain Event
    “A domain object that de
    fi
    nes an event
    (something that happens). A domain event is an
    event that domain experts care about.”

    View full-size slide

  44. Domain Event != Event Souring

    View full-size slide

  45. Kitty Event
    s

    https://github.com/producthunt/KittyEvents

    View full-size slide

  46. Design Patterns
    🚗 Entity

    🚕 Value Object

    🚙 Aggregate


    🚌 Repository


    🚎 Facade


    🚐 Factory


    🚚 Service

    🚛 Presenter

    View full-size slide

  47. Design Patterns
    🚗 Entity

    🚕 Value Object

    🚙 Aggregate


    🚌 Repository


    🚎 Facade


    🚐 Factory


    🚚 Service

    🚛 Presenter

    View full-size slide

  48. Design Patterns
    🚗 Entity

    🚕 Value Object

    🚙 Aggregate


    🚌 Repository


    🚎 Facade


    🚐 Factory


    🚚 Service

    🚛 Presenter

    View full-size slide

  49. https://github.com/producthunt/producthunt/tree/master/app/services/sharing
    👉
    https://github.com/producthunt/stacks/tree/master/backend/app/services/share

    View full-size slide

  50. module Share::Subject
    extend self
    def call(subject:, current_profile:
    )

    subject_class = subject.class.name.tr('::', ''
    )

    share = "Share::Subjects::#{subject_class}".constantiz
    e

    share.generate subject, current_profil
    e

    end
    end

    View full-size slide

  51. module Share
    extend self
    def message_for(subject, current_profile: nil
    )

    subject_class = subject.class.name.tr('::', ''
    )

    share = "Share::Subjects::#{subject_class}".constantiz
    e

    share.generate subject, current_profil
    e

    end
    end

    View full-size slide

  52. module Share
    extend self
    def message_for(subject, current_profile: nil
    )

    subject_class = subject.class.name.tr('::', ''
    )

    share = "Share::Message::#{subject_class}".constantiz
    e

    share.generate subject, current_profil
    e

    end
    end

    View full-size slide

  53. module Share
    extend self
    def message_for(subject, current_profile: nil
    )

    share = Utils.module_for(subject, in: Share::Message
    )

    share.generate subject, current_profil
    e

    end
    end
    💡

    View full-size slide

  54. Whats next...

    View full-size slide

  55. ... any ideas? 💡 😅

    View full-size slide