Skip to content

Latest commit

Β 

History

204 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸš€ Ruby DDD CQRS - Chat Application

Ruby Rails PostgreSQL Redis RabbitMQ Kafka Docker RSpec

A modern, scalable chat application built with Domain-Driven Design (DDD) and Command Query Responsibility Segregation (CQRS) patterns using Ruby on Rails 8.

πŸ—οΈ Architecture Overview

This application demonstrates clean architecture principles with a clear separation of concerns:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    Frontend Layer                           β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                  Controllers (Rails)                        β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                Application Layer (CQRS)                     β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                   β”‚
β”‚  β”‚   Commands      β”‚  β”‚     Queries     β”‚                   β”‚
β”‚  β”‚ (Write/Postgres)β”‚  β”‚ (Read/Redis)    β”‚                   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                  Domain Layer (DDD)                         β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                   β”‚
β”‚  β”‚   Aggregates    β”‚  β”‚  Value Objects  β”‚                   β”‚
β”‚  β”‚   Domain Events β”‚  β”‚   Repositories  β”‚                   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚               Infrastructure Layer                          β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                   β”‚
β”‚  β”‚   PostgreSQL    β”‚  β”‚      Redis      β”‚                   β”‚
β”‚  β”‚   RabbitMQ      β”‚  β”‚     Gemini      β”‚                   β”‚
β”‚  β”‚   Kafka         β”‚  β”‚ Kafka Connect   β”‚                   β”‚
β”‚  β”‚   (Redpanda)    β”‚  β”‚  (JDBC sink)    β”‚                   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ“Š Analytics Pipeline

The analytics bounded context follows an event-driven Message State flow instead of in-memory projections:

 MessageSent
  domain event
      β”‚
      β–Ό
 MessageStatePublisher
  (Kafka producer)
      β”‚
      β–Ό
 chat.message.state   (compacted topic)
      β”‚
      β–Ό
 Kafka Connect        (JDBC sink β†’ analytics DB)
      β”‚
      β–Ό
 message_records      (analytics Postgres DB)
      β”‚
      β–Ό
 SQL Read Models      (ChatActivity / UserEngagement)

Rails reads analytics from a dedicated analytics database managed via Rails multi-database, while the JDBC sink only writes β€” the Rails schema owns the table.

✨ Key Features

  • 🎯 Domain-Driven Design: Clean domain models with aggregates, value objects, and domain events
  • ⚑ CQRS Pattern: Separate command and query models for optimal performance
  • πŸ—οΈ Clean Architecture: Clear separation between domain, application, and infrastructure layers
  • πŸ“‘ Event-Driven Analytics: Message state published to a compacted Kafka topic and synced via Kafka Connect JDBC sink
  • πŸ—„οΈ Multi-Database: Dedicated analytics database integrated with Rails multi-database
  • 🐳 Docker Support: Complete containerized development environment
  • πŸ“± Real-time Chat: WebSocket support with Action Cable
  • πŸ§ͺ Comprehensive Testing: RSpec test suite with proper test isolation
  • πŸ”’ Security: Brakeman security scanning and best practices

πŸ› οΈ Technology Stack

Backend

  • Ruby 3.4.10 - Ruby programming language
  • Rails 8.1.2 - Ruby on Rails framework
  • PostgreSQL 15 - Robust relational database (primary + analytics)
  • Redis 7.2.0 - In-memory data structure store
  • RabbitMQ 3.12 - Message broker for event-driven architecture
  • Redpanda (Kafka) - Kafka-compatible message streaming platform
  • Kafka Connect - JDBC sink connector that streams message state into the analytics database
  • Gemini - LLM / Embeddings service used for RAG (Retrieval-Augmented Generation)

Development & Testing

  • RSpec - Framework for testing
  • RuboCop - Ruby code style checker
  • Brakeman - Security vulnerability scanner
  • Faker - Test data generation
  • Docker Compose - Multi-container development environment

Architecture Patterns

  • Domain Events - Event-driven communication between aggregates
  • Aggregate Roots - Consistency boundaries for domain objects
  • Value Objects - Immutable domain concepts
  • Repository Pattern - Domain-focused data access abstraction that acts as a collection of aggregates and hides infrastructure details.
  • Command/Query Separation - Optimized read/write operations

πŸš€ Quick Start

Prerequisites

  • Ruby 3.4.10
  • Docker & Docker Compose
  • PostgreSQL client (optional)

1. Clone the Repository

git clone https://github.com/mapeveri/ruby-ddd-cqrs.git
cd ruby-ddd-cqrs

2. Environment Setup

cp env.example .env
# Edit .env with your configuration

3. Start Services with Docker

docker-compose up -d

4. Install Dependencies

bundle install

5. Database Setup

Creates and migrates both the primary and analytics databases:

bin/rails db:create db:migrate

6. Bootstrap Kafka Infrastructure

# Creates the compacted `chat.message.state` topic
bin/bootstrap_kafka.rb

# Registers the JDBC sink connector
bin/register_sink_connector.sh

7. Start the Application

bin/rails server

Visit http://localhost:3000 to see your application!

Backfilling Message State

Republish the full state of every existing message to the Kafka topic (idempotent upsert):

bin/backfill_messages.rb

Consuming Messages

Kafka (Redpanda) - inspect the message state topic using rpk:

# Follow new messages as they arrive
docker exec -it redpanda rpk topic consume chat.message.state

# Read the topic from the beginning
docker exec -it redpanda rpk topic consume chat.message.state -o beginning

# List all topics
docker exec -it redpanda rpk topic list

Note: The JDBC sink connector (messages-jdbc-sink) consumes the topic automatically and persists every message into the analytics database (message_records). The rpk commands above are only for inspecting/debugging raw messages β€” they are not part of the persistence flow.

RabbitMQ - consume domain events and dispatch them to their handlers:

bin/rabbit_mq_consumer

πŸ›οΈ Project Structure

src/
β”œβ”€β”€ shared/                    # Shared domain components
β”‚   β”œβ”€β”€ domain/
β”‚   β”‚   β”œβ”€β”€ aggregate_root.rb  # Base aggregate root class
β”‚   β”‚   β”œβ”€β”€ domain_events/     # Domain event infrastructure
β”‚   β”‚   β”œβ”€β”€ bus/               # Event bus implementation
β”‚   β”‚   └── value_objects/     # Shared value objects
β”‚   └── infrastructure/        # Shared infrastructure
β”‚       └── messaging/kafka/   # Kafka client wrapper
β”œβ”€β”€ chat/                      # Chat bounded context
β”‚   β”œβ”€β”€ domain/                # Domain layer
β”‚   β”‚   β”œβ”€β”€ message/           # Message aggregate
β”‚   β”‚   └── user/              # User aggregate
β”‚   β”œβ”€β”€ application/           # Application layer (CQRS)
β”‚   β”‚   └── message/
β”‚   β”‚       β”œβ”€β”€ commands/      # Write operations
β”‚   β”‚       └── queries/       # Read operations
β”‚   └── infrastructure/        # Infrastructure layer
β”‚       β”œβ”€β”€ messaging/kafka/   # Message state publisher
β”‚       └── subscribers/       # Domain event subscribers
└── analytics/                 # Analytics bounded context
    β”œβ”€β”€ domain/                # Domain layer
    β”œβ”€β”€ application/           # Queries (read models)
    └── infrastructure/
        └── persistence/
            β”œβ”€β”€ redis/         # Caching
            └── analytics_db/  # Message state records + SQL read models

db/
β”œβ”€β”€ migrate/                   # Primary database migrations
β”œβ”€β”€ analytics_migrate/         # Analytics database migrations
└── analytics_schema.rb        # Analytics database schema

docker/
β”œβ”€β”€ kafka-connect/             # Kafka Connect image + connector config
└── analytics_db/              # Analytics DB init scripts

πŸ§ͺ Testing

Run the complete test suite:

bundle exec rspec

Run specific test files:

bundle exec rspec spec/chat/domain/message

🐳 Docker Development

Services

  • PostgreSQL (primary): localhost:5435
  • PostgreSQL (analytics): localhost:5436
  • Redis: localhost:6379
  • Redis Test: localhost:6380
  • RabbitMQ: localhost:5672
  • RabbitMQ Management: localhost:15672
  • Redpanda (Kafka): localhost:9092
  • Kafka Connect REST API: localhost:8083

Useful Commands

# View logs
docker-compose logs -f

# Restart services
docker-compose restart

# Stop all services
docker-compose down

# Rebuild containers
docker-compose up --build

# Inspect the analytics connector status
curl localhost:8083/connectors/messages-jdbc-sink/status

🀝 Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

About

Api Rest with Ruby on Rails + Hexagonal Architecture + DDD & CQRS.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages