Public repo 2024

Rails modular monolith with DDD

Packwerk boundaries, a transactional outbox and a Kafka consumer in Rails.

A worked example of splitting a Rails application into Packwerk-bounded components, with a transactional outbox so an event written in the same transaction as the data is never lost before it is published. Debezium tails the outbox table through Kafka Connect, a Karafka consumer processes the event with a dead-letter queue, and the AI component answers through a local Ollama model and keeps its own database.

A diagram of the reference implementation's four domains, each with its own database; a write that lands in a transactional outbox table inside the same transaction; Kafka Connect tailing that table; and events published to Kafka for the other domains to consume. The Rails port implements the Packwerk boundaries, the outbox and the Kafka path, but not the per-domain databases or the reference's sagas.
The reference implementation's four-domain shape, which the Rails port starts from.

What it does

  • Domain boundaries enforced by Packwerk rather than by folder naming.
  • A transactional outbox so an event cannot be lost between commit and publish.
  • Kafka Connect tailing the outbox table and pushing events onto Kafka.
  • A Karafka consumer for the AI component's outbox events, with a dead-letter queue.
  • The AI component keeps its own database through Rails' multiple-database connection.
  • A generative-AI component wired to a local Ollama model.

Built with

  • Ruby on Rails
  • Packwerk
  • Kafka
  • Karafka
  • Ollama
  • DDD

Writing