Because every Wedding RSVP website needs to follow DDD, CQRS, Hexagonal Architecture, Event Sourcing, and be deployed on Lambda.

Overview

Our Wedding Website

Because every Wedding RSVP website needs to follow DDD, CQRS, Hexagonal Architecture, Event Sourcing, and be deployed on Lambda.

🌎 Website | 🏗️ Infrastructure

Overview

This application (and associated infrastructure) documents an approach to building complex systems which require the benefits that DDD, Hexagonal Architecture and Event Sourcing provide. On-top of this it shows how such an application can be combined with Terraform and deployed in a Serverless manor.

Using PHP and the Symfony framework it highlights how such an approach can be laid out, coupled with a sufficient testing strategy and local development environment. Some topics and features covered within this application are:

  • Use of PHP 8.1 and Bref for Serverless Lambda environment.
  • Docker-based local development environment, which replicates the intended Lambda platform.
  • GNU make used to assist in running the application locally and performing CI-based tasks.
  • CI pipeline developed using GitHub workflows, running the provided tests and deploying the application to the given stage-environments (staging and production).
  • Implements the desired Message buses using Symfony Messenger, with asynchronous transport being handled by SQS/Lambda.
  • Runtime secrets pulled in via Secrets Manager (and cached using APCu) using a Symfony environment variable processor.
  • Email communication sent using Symfony Mailer (via Gmail), with local testing achieved using MailHog.
  • Webpack Encore used to transpile and bundle assets (TypeScript, CSS and Images) used throughout the website.
  • Event stream snapshots generated and validated within Application level tests, providing regression testing for present stream structures.
  • Automated aggregate event diagrams created using the Application level Event stream snapshots, combined with Graphviz.
  • Automated documentation/diagrams generated for the present Commands within the system.
  • Use of Deptrac to ensure that desired Hexagonal Architecture layering is maintained.
  • Psalm and PHP Coding Standards Fixer employed to ensure correctness and coding standards maintained.
  • DynamoDB configured to manage client sessions within a deployed stage-environment.

Getting Started

Prerequisite: ensure you have Docker installed on your local machine.

make start
make open-web
make can-release

All available actions within the local development environment are available (and documented) within the Makefile by running make help.

Architecture

The application follows CQRS for interaction between the Ui and Application, Hexagonal Architecture to decouple the Infrastructural concerns, and DDD/Event Sourcing to model the Domain.

Layers

Following Hexagonal Architecture, the layers have been defined like so:

Communication Flow

Based on the above layers, we employ three distinct message buses (Command, Aggregate Event and Domain Event), modeling the Aggregates using Event Sourcing. The following diagram highlights how these three buses interact during a typical Command/Query lifecycle response.

Aggregate Events

There are two Aggregates within the Domain (FoodChoice and Invite), the Aggregate Event flow for both goes as follows:

This diagram is automatically generated based on the current implementation, using testable Event snapshots at the Command level.

Commands and Domain Events

Application-level Commands which are available for the Ui to interact with the Domain are presented below:

Along with the Command and Command Handlers, this also deptics the associated Domain Events which are emitted.

Testing

The testing strategy employed within this application helps aid in following a Test Pyramid, favouring testing behaviour over implementation. In doing this we exercise most of our behavioral assertions at the Application layer, testing the public API provided by the Commands and Query services. This provides us with a clear description of the intended application's behaviour, whilst reducing the brittleness of the given tests as only public contracts are used.

Testing has been broken up into a similar Hexagonal Architecture layered representation as the system itself, like so:

Domain

Low-level domain testing which is heavily coupled to the current implementation. This is used in cases where you wish to have a higher-level of confidence of a given implementation which can not be easier asserted at the Application level.

Application

Unit tests (ala Unit of behaviour) which use the public API exposed by the Commands and Query services to assert correctness. These are isolated from any infrastructural concerns (via test doubles) and exercise the core business logic/behaviour that the application provides. This level provides us with the greatest balance between asserting that the current implementation achieves the desired behaviour, whilst not being over-coupled to the implementation causing the test to become brittle. Depending only on the public API within these tests allows us to refactor the underlying Domain implementation going forward whilst keeping the tests intact. As such, it is desired to have the most amount of testing at this level.

Infrastructure

Contractual tests to assert that the given adaptor implementation completes the required ports responsibility; communicating with external infrastructure (such as a database) in isolation to achieve this.

Ui

Full-system tests which exercises the entire system by-way of common use-cases. This provides us with confidence at the highest-level that the application is achieving the desired behaviour.

Linting

The application uses the following linting tools to maintain the desired code quality and application correctness.

  • Psalm - used to provide type-checking support within PHP (app/psalm.xml).
  • PHP Coding Standards Fixer - ensures the desired PHP code styling is maintained (app/.php-cs-fixer.php).
  • Deptrac - ensures we adhere to the strict Hexagonal Architectural layering boundaries we have imposed (depfile.yml).
  • Local PHP Security Checker - ensures that no known vulnerable dependencies are used within the application.
  • Prettier - ensures the desired JS code style is maintained (app/package.json).

These tools can be run locally using make lint, returning a non-zero status code upon failure. This process is also completed during a make can-release invocation.

Infrastructure

The application is hosted on AWS Lambda with transient infrastructure (which change based on each deployment) being provisioned using the Serverless Framework. Resources managed at this level include Lambda functions, API-gateways and SQS event integrations. Foundational infrastructural concerns (such as networking, databases, queues etc.) are provisioned using Terraform and can be found in the related repository.

Sharing between Terraform and Serverless Framework is unidirectional, with the application resources that Serverless Framework creates being built upon the foundation that Terraform resources provision. Parameters, secrets and shared resources which are controlled by Terraform are accessible to this application via SSM parameters and Secrets Manager secrets; providing clear responsibility separation.

Backends

The application consists of an Event Store which persist aggregate events produced by invoked Commands. It also includes Projections which persist materialised views of these aggregate events, accessible via Query services. These two responsibilities do not require a shared data-store, and can manage their own state as best they see fit.

To highlight this, I have built several persistence implementations of both the Event Store and Projections. This exercises the importance of good abstraction, and the benefits of layering your application using Hexagonal Architecture (Ports and Adaptors). Although only one is used within the production setting (Postgres), as a local demonstration it is interesting to see the differences.

Event Store

Using the provided Event Store port interface, there are the following implementations:

  • Postgres

    • Stores the events in sequential order within a single-table.
    • Ensures domain aggregate invariance, using version constraints to prevent competing client updates.
    • Uses a transaction to ensure that all aggregate events are persisted to the event stream in one atomic operation.
    • Using a single-table design (with a sequential ordering) allows for trivial event store streaming capabilities.
  • DynamoDB

    • Stores the events within a single-table, with the aggregate identifier and version mapping well to DynamoDB's partition and sort keys respectively.
    • Ensures domain aggregate invariance, using DynamoDB write constraints to prevent competing client updates.
    • The aggregate events are currently not persisted within a single write transaction, as the underlying client (AsyncAWS) does not support this yet.
    • The sequential ordering is carried out using the event creation time within micro-seconds. Due to DynamoDB's behavioral properties, you are unable to maintain a sequential auto-incrementing identifier. Although not ideal, this is the best we can attain from using such a means.
    • Currently, a hot partition key is used based on this event creation time to provide the same sequential ordering required for streaming.

Future developments that I wish to consider, are to instead store the events within S3 for persistence (possibly via DynamoDB streams for durability), and then query for these using Athena when streaming operations are required. In doing this we would move away from the hot key that has been introduced in its current form.

  • EventStoreDB

    • Communicates with the EventStoreDB over HTTP via Atom.
    • Separate event streams are created per aggregate, following the naming convention #AGGREGATE_NAME#-#AGGREGATE_ID#.
    • Ensures domain aggregate invariance, using the event streams expected version constraints which are provided by the persistence layer.

With the HTTP and Atom communication protocols being deprecated, work to replace this with a gRPC client written in PHP would need to be carried out before putting such an implementation in production.

Projections

You might also like...
FFCMS 3 version core MVC architecture. Build-on use with ffcms main architecture builder.

FFCMS 3 version core MVC architecture. Build-on use with ffcms main architecture builder.

FalconOne Lite is an Open Source solution deployed and updated on a daily basis to help prevent terror and crime globally
FalconOne Lite is an Open Source solution deployed and updated on a daily basis to help prevent terror and crime globally

FalconOne Lite is an Open Source solution deployed and updated on a daily basis to help prevent terror and crime globally. By using advanced tools, functions and stealth strategies, FalconOne community is focused on making a friendly and fast solution for effective results.

Do you want CronJob to follow the solar date?You need this package to solve this problem.
Do you want CronJob to follow the solar date?You need this package to solve this problem.

Shamsic Maybe it happened to you that you wanted to use CronJob in your project and you realized that you cannot manage the exact dates that are in th

Dedicated plugin for PocketMine-API 4, this will help staff members to make players follow the rules to the letter

StaffMode Dedicated plugin for PocketMine-API 4, this will help staff members to make players follow the rules to the letter Annotation This plugin is

Made a custom Elementor widget because i wasn't able to find any free version of a slider with thumbnails.
Made a custom Elementor widget because i wasn't able to find any free version of a slider with thumbnails.

Support my work ❤️ Buy me a coffee ☕ How to install Download - place in plugin folder - Activate under WordPress dashboard. How to use Open Elemento

Lambda calculus interpreter in PHP.
Lambda calculus interpreter in PHP.

lambda-php Lambda calculus interpreter in PHP. Lambda calculus Lambda calculus is a very minimal programming language that was invented in 1936 by Alo

λ Run PHP Coroutines & Fibers as-a-Service on the AWS Lambda.
λ Run PHP Coroutines & Fibers as-a-Service on the AWS Lambda.

λ Swoole Runtime for AWS Lambda Run PHP Coroutines & Fibers as-a-Service on the AWS Lambda. Getting started Create your Lambda function index.php ?ph

PHP Runtime Layer for AWS Lambda

PHP Layer For AWS Lambda Ever wanted to run PHP websites in AWS Lambda? It's your lucky day! This Lambda Runtime Layer runs the PHP 7.3/7.1 webserver

A tool to create php lambda's in AWS via custom runtime api

Getting Started This composer library assists in the creation, configuration, and testing of an AWS Lambda function. It utilizes the AWS Lambda custom

Owner
Edd Mann
Edd Mann
A Symfony project made with DDD, CQRS and Hexagonal Architecture

Symfony Blog DDD + CQRS + Hexagonal Architecture A Symfony blog project made with CQRS, Hexagonal Architecture and DDD Docker integration This project

null 5 Aug 10, 2022
🐘 🎯 Hexagonal Architecture, DDD & CQRS in PHP

?? ?? Hexagonal Architecture, DDD & CQRS in PHP Example of a PHP application using Domain-Driven Design (DDD) and Command Query Responsibility Segrega

CodelyTV 2.5k Jan 6, 2023
Clean Architecture, DDD and CQRS using Symfony 6

Task manager system using Clean Architecture, DDD and CQRS. Environment setup Install Docker Clone the project: git clone https://github.com/k0t9i/Tas

null 3 Sep 5, 2022
Dockerise Symfony Application (Symfony 6 + Clean Architecture+ DDD+ CQRS + Docker + Xdebug + PHPUnit + Doctrine ORM + JWT Auth + Static analysis)

Symfony Dockerise Symfony Application Install Docker Install Docker Compose Docker PHP & Nginx Create Symfony Application Debugging Install Xdebug Con

null 48 Jan 5, 2023
Enraged Xenomorph - DDD/CQRS Symfony Application Boilerplate

Enraged Xenomorph - DDD/CQRS Symfony Application Boilerplate This project is very opinionated attempt to compile a bit of experience, few good practic

Gniewomir Świechowski 1 Jan 10, 2022
Orkestra is a library of infrastructure and architecture helpers for creating CQRS applications

Orkestra Orkestra is an opinionated framework with a plethora of recommendations on architectural design that we use internally at Morebec to develop

Morébec 2 Aug 18, 2021
Psalm plugin for patchlevel/event-sourcing

event-sourcing-psalm-plugin psalm plugin for event-sourcing library. installation composer require --dev patchlevel/event-sourcing-psalm-plugin confi

patchlevel 4 Dec 14, 2022
A "from scratch" PHP-based implementation of Event-Sourcing

In here, you will find a "from scratch" PHP-based implementation of Event-Sourcing, kept to a minimum on purpose, to allow workshop attendees to explore and experiment with its concepts.

Marco Pivetta 94 Jan 1, 2023
The easiest way to get started with event sourcing in Laravel

Event sourcing for Artisans ?? This package aims to be the entry point to get started with event sourcing in Laravel. It can help you with setting up

Spatie 591 Jan 4, 2023
POC d'un projet Clean Architecture + DDD

Proof Of Concept - Clean Architecture & DDD Installation Dans un premier temps, cloner le repository : git clone https://github.com/TBoileau/rse cd rs

Thomas Boileau 11 Sep 3, 2022