Domain Driven Design
Domain Driven Design
Domain Driven Design advocates for organizing your code around how your business and its workflows are organized.
Wikipedia Page: https://en.wikipedia.org/wiki/Domain-driven_design
It's difficult to map domain driven design to a specific code implementation because it's a concept, not a software framework. However, important parts of the concept include:
Entities
An object with a unique identifier. Its attributes may change but the unique identifier lets us continue to acknowledge it as the same entity.
In Ruby on Rails, this could be an ActiveRecord model
create_table :pizzas do |t|
t.uuid :uuid
t.string :size
t.expiration_date :datetime
end
class Pizza << ActiveRecord::Base
end
In this case, the size and expiration_date may change but the uuid tells us it is the same object.
Value Objects
An object without a unique identifier whose attributes determine its value.
In Ruby on Rails, this could be a Money object
value_object_one = Money.new("USD", 10.10) # Currency, Magnitude
value_object_two = Money.new("USD", 10.10) # Currency, Magnitude
value_object_one == value_object_two # would return true
In this case, the combination of currency and magnitude determines what the object represents. Any two instances of the money class with the same attributes are equal.
Aggregates
An abstraction that guarantees the consistency of changes to a collection of entities.
In Ruby on Rails, this could be a custom class that exacts as an interface for making changes to multiple ActiveRecord models which need to be updated together.
class Order
def place(opts)
ActiveRecord::Base.transaction do
if opts[:pizza]
Pizza.create!(opts[:pizza])
end
if opts[:soda]
Soda.create!(opts[:soda])
end
end
end
end
Services
A service handles the use of aggregates. It's an interface into the domain logic of your codebase.
In Ruby on Rials, this could look like the following:
class OrdersController
def create
ProcessPayment.call(params)
PlaceOrder.call(params)
SendConfirmation.call(params)
end
end
class PlaceOrder
def self.call(opts)
order = Order.new(opts)
order.place
end
end
These are simple examples that cannot illustrate the full reasons why you would want to apply domain driven design to your codebase. In order to learn more, please see the following resources:
Articles
- https://blog.thelonearchitect.com/a-gentle-introduction-to-domain-driven-design-dc7cc169b1d
Books
- Domain-Driven Design by Eric Evans
- Domain-Driven Design Distilled by Vaughn Vernon
- Implementing Domain-Driven Design by Vaughn Vernon
Sample Applications
- https://github.com/kgrzybek/modular-monolith-with-ddd
Talks/Videos
- RailsConf 2014 - Domain Driven Design and Hexagonal Architecture with Rails (https://www.youtube.com/watch?v=_rbF97T4480)