Using DDD to build a bookstore


Created by Stan on 07-04-2023


A brief intro

Domain-Driven Design is a thrilling ride filled with magical concepts and endless possibilities. We'll use a simple online bookstore domain as an example. By embracing DDD as your trusty guidebook and incorporating patterns like the Repository Pattern, you can explore complex software with confidence, creating flexible, scalable, and maintainable software systems that accurately represent the relevant business logic.

Our project

For our purposes, we'll build a simple bookstore. We will design the models as shown in the YML diagram below. The Author, Book, and Price classes are represented with their attributes and methods. The relationships between these classes are shown with associations: An Author is associated with zero or more Books, while each Book is associated with exactly one Author and one Price.

DDD bookstore model

Let's start coding

We will create an Author entity, a Book entity, and a Price value object.

# author.rb
class Author
  attr_reader :id, :name

  def initialize(id, name)
    @id = id
    @name = name
  end
end

# book.rb
class Book
  attr_reader :id, :title, :author, :price

  def initialize(id, title, author, price)
    @id = id
    @title = title
    @author = author
    @price = price
  end
end

# price.rb
class Price
  attr_reader :amount, :currency

  def initialize(amount, currency)
    @amount = amount
    @currency = currency
  end

  def ==(other)
    other.is_a?(Price) && amount == other.amount && currency == other.currency
  end
end

Now, let's implement the Repository Pattern to handle storage and retrieval of Book entities. We'll create an interface for the BookRepository and an implementation of the repository interface.

#book_repository.rb

module BookRepository
  def all; end
  def find_by_id(id); end
  def add(book); end
  def remove(book); end
end

# book_repository_impl.rb

require_relative 'book_repository'

class BookRepositoryImpl
  include BookRepository

  def initialize
    @books = []
  end

  def all
    @books
  end

  def find_by_id(id)
    @books.find { |book| book.id == id }
  end

  def add(book)
    @books << book
  end

  def remove(book)
    @books.delete(book)
  end
end

With the BookRepositoryImpl, we can now store and retrieve Book entities:

# client.rb

require_relative 'author'
require_relative 'price'
require_relative 'book'
require_relative 'book_repository_impl'

# Creating some sample data
author = Author.new(1, 'J.K. Rowling')
price = Price.new(10.99, 'USD')
book = Book.new(1, 'Harry Potter and the Philosopher\'s Stone', author, price)

# Using the InMemoryBookRepository
repository = BookRepositoryImpl.new
repository.add(book)

puts repository.all.inspect # => [#<Book:0x00005607c9a9e990 @id=1, @title="Harry Potter and the Philosopher's Stone", @author=#<Author:0x00005607c9a9e9b8 @id=1, @name="J.K. Rowling">, @price=#<Price:0x00005607c9a9e9e0 @amount=10.99, @currency="USD">>]

found_book = repository.find_by_id(1)
puts found_book.inspect # => #<Book:0x00005607c9a9e990 @id=1, @title="Harry Potter and the Philosopher's Stone", @author=#<Author:0x00005607c9a9e9b8

repository.remove(book)
puts repository.all.inspect # => []

In a real-world application, you might implement additional repositories, such as a DatabaseBookRepository, to interact with a database or another storage system. This separation of concerns allows you to swap out different implementations of the repository without affecting the rest of your application.

The Repository Pattern provides a clean and maintainable way to manage the storage and retrieval of domain entities. It abstracts the data storage mechanism from the domain logic, making it easier to adapt to changes in storage requirements and promoting a modular and well-structured codebase.

By embracing DDD as your trusty guidebook and incorporating patterns like the Repository Pattern, you can explore complex software kingdoms with confidence, creating flexible, scalable, and maintainable software systems that accurately represent the diverse realms and their inhabitants. As you embark on your DDD journey, remember to collaborate closely with domain experts, forge a shared understanding of the realms, and utilize the mighty building blocks of DDD to conquer the challenges ahead.



Related Posts

Architecture

Posted on 08 Apr, 2023
How to create a binary search tree in Ruby

Architecture

Posted on 10 Apr, 2023
Rate limiting in Ruby using Sinatra and Redis