What's inside the Rails DDD workshop application?

… and check why 5600+ Rails engineers read also this

What’s inside the Rails DDD workshop application?

An integral part of our Rails DDD workshops is an application which we use during the teaching and exercises process.

Many people have asked what’s inside the app, so I have prepared a small sneak-peek.

The UI

Let’s start with the UI to see the scope of the app.

There are typical Rails scaffold CRUD UIs for customers and products, respectively:

In the above screens we can manage and prepare customers and products, which will be used in other parts of the system.

The order list screen lets us review the orders, which is the main part of the system:

As you can see, there are some features of what we can do with the order: pay, ship, cancel, history.

Creating a new order screen displays the existing products and lets us choose a customer.

This screen simulates the payment, to show how we can integrate with external API.

The history view shows the events related to that order, which makes debugging easier, we can the whole history here.

The routes/controllers


Rails.application.routes.draw do
  root to: 'orders#index'
  resources :orders, only: [:index, :show, :new, :create, :destroy] do
    get  :pay
    post :ship
  end
  resources :payments, only: [:create]

  resources :customers, only: [:index, :show, :new, :edit, :create, :update]
  resources :products
end

The domain

Given that this app helps learning DDD, you could expect some interesting domain layer, right?

In this case there are 2 domain-rich bounded contexts, each of them represented as a Ruby namespace:

  • Orders
  • Payments

and there are Products and Customers which we could probably also call like Catalog and CRM respectively, but here they are just CRUD contexts, without much logic.

We’ve used the Product and Customer ActiveRecord-driven CRUDs to represent how such things can cooperate with domain-rich bounded contexts.

We also have one saga (or process manager, depending on the definition), called Discount.

There’s also a projection, called PaymentsProjection.

In the spirit of CQRS, we handle the “write” part with Commands.


module Payments
  class AuthorizePaymentCommand
    include Command

    attr_accessor :order_number
    attr_accessor :total_amount
    attr_accessor :card_number

    validates_presence_of :order_number, :total_amount, :card_number
  end
end

Everything is based on events, through which the different contexts communicate with each other.


  class PaymentReleased < RubyEventStore::Event
    SCHEMA = {
      transaction_identifier: String,
      order_number:  String,
    }.freeze

    def self.strict(data:)
      ClassyHash.validate(data, SCHEMA, true)
      new(data: data)
    end
  end

There are aggregates for Payment and for Order.

All the domain logic of the application is fully tested.

module Orders
  RSpec.describe Order do
    it 'newly created order could be expired' do
      order = Order.new(number: '12345')
      expect{ order.expire }.not_to raise_error
      expect(order).to publish [
        OrderExpired.strict(data: { order_number: '12345' }),
      ]
    end

The CQRS/EventSourcing infra code

The app is a nice example of a non-trivial code which is using the RailsEventStore ecosystem of tools.

The exercises

The code is hosted on GitLab. Once you get access there, you will also see a list of Issues. Each issue is actually an exercise to let you practice DDD, based on this app. Several of those exercises are what we expect you to do, during the workshops (with our support and help).

Summary

I hope this blogpost answers some questions and can help you evaluate whether our Rails DDD workshops are of value to you.

You might also like