Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@ on:

jobs:
test:
name: Test on PHP ${{ matrix.php-version }}
runs-on: ubuntu-latest

strategy:
matrix:
php-version: [ '8.1', '8.2', '8.3' ]
php-version: [ '8.2', '8.3', '8.4' ]

steps:
- name: Checkout source code
Expand All @@ -28,7 +29,7 @@ jobs:
tools: composer:v2

- name: Cache dependencies
uses: actions/cache@v2
uses: actions/cache@v4.3.0
with:
path: ~/.composer/cache
key: php-${{ matrix.php-version }}-composer-${{ hashFiles('**/composer.json') }}
Expand All @@ -37,9 +38,15 @@ jobs:
- name: Validate composer.json and composer.lock
run: composer validate

- name: Install dependencies
- name: Install Dependencies
if: steps.composer-cache.outputs.cache-hit != 'true'
run: composer install --prefer-dist --no-progress --no-suggest
run: composer install --prefer-dist --no-progress

- name: Check Code Style
run: composer pint-test

- name: Execute Static Code analysis
run: composer analyse
run: composer analyse

- name: Execute Unit, Integration and Acceptance Tests
run: composer test
10 changes: 8 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
composer.phar
/vendor/
.idea/
/infrastructure
/infrastructure/
/coverage/
*[N|n]o[G|g]it*
*coverage*
coverage.xml
test.xml
.phpunit.result.cache
composer.lock
wiki/Home.md
/.phpunit.cache
.phpactor.json
/.serena/
79 changes: 59 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,62 @@

Common interfaces for PHP Complex Heart SDK.

## Domain Modeling

- Aggregate
- Entity
- ValueObject
- Identity

## Service Bus

- ServiceBus
- Command
- CommandBus
- CommandHandler
- Event
- EventBus
- EventHandler
- Query
- QueryBus
- QueryHandler
- QueryResponse
## Domain Layer

### Model
Core building blocks for domain-driven design:
- **Aggregate** - Root entity with domain event publishing
- **Entity** - Domain object with unique identity
- **ValueObject** - Immutable domain value with equality
- **Identifier** - Unique identifier representation

### Events
Domain event interfaces following ISP (Interface Segregation Principle):
- **Event** - Base domain event (eventId, eventName, payload, occurredOn)
- **Traceable** - Distributed tracing (correlationId, causationId)
- **Sourceable** - Event sourcing (aggregateId, aggregateType, eventVersion)
- **EventBus** - Publishes domain events

## Application Layer

### Command
Write operations (CQRS):
- **Command** - Marker interface for state-changing operations
- **CommandBus** - Dispatches commands to handlers
- **CommandHandler** - Executes commands

### Query
Read operations (CQRS):
- **Query** - Marker interface for data retrieval
- **QueryResponse** - Marker interface for query results
- **QueryBus** - Routes queries to handlers
- **QueryHandler** - Executes queries and returns responses

### Handler
Event handlers:
- **EventHandler** - Reacts to domain events

### Service Bus
Unified message bus facade:
- **ServiceBus** - Provides access to CommandBus, QueryBus, and EventBus

## Architecture

This library follows Clean Architecture principles with explicit layer separation:
- **Domain → Application** - Domain layer is independent, Application depends on Domain
- **Layer-Explicit Namespaces** - Clear architectural boundaries in namespace structure
- **Interface Segregation** - Compose only needed capabilities (e.g., Event + Traceable + Sourceable)

### Architecture Testing

The project includes automated architecture tests using Pest PHP:

```bash
composer test
```

Tests enforce:
- Domain layer independence (no Application dependencies)
- Correct interface placement and usage
- Clean Architecture dependency rules
- PHP and security best practices (via arch presets)
13 changes: 10 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,29 @@
],
"minimum-stability": "stable",
"require": {
"php": "^8.1"
"php": "^8.2"
},
"require-dev": {
"phpstan/phpstan": "^1.0",
"phpstan/extension-installer": "^1.3"
"phpstan/extension-installer": "^1.3",
"laravel/pint": "^1.25",
"pestphp/pest": "^3.8.4",
"pestphp/pest-plugin-arch": "^3.1.1"
},
"autoload": {
"psr-4": {
"ComplexHeart\\": "src/"
}
},
"scripts": {
"analyse": "vendor/bin/phpstan analyse src --no-progress --level=9"
"test": "vendor/bin/pest --configuration=phpunit.xml --coverage --coverage-clover=coverage.xml --log-junit=test.xml",
"analyse": "vendor/bin/phpstan analyse src --no-progress --memory-limit=4G --level=9",
"pint-test": "vendor/bin/pint --preset=psr12 --test",
"pint": "vendor/bin/pint --preset=psr12"
},
"config": {
"allow-plugins": {
"pestphp/pest-plugin": true,
"phpstan/extension-installer": true
}
}
Expand Down
23 changes: 23 additions & 0 deletions phpunit.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
bootstrap="vendor/autoload.php"
colors="true"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd"
cacheDirectory=".phpunit.cache">
<coverage>
<report>
<clover outputFile="./coverage.xml"/>
</report>
</coverage>
<testsuites>
<testsuite name="unit">
<directory>./tests</directory>
</testsuite>
</testsuites>
<logging/>
<source>
<include>
<directory suffix=".php">./src</directory>
</include>
</source>
</phpunit>
17 changes: 17 additions & 0 deletions src/Application/Command/Command.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace ComplexHeart\Application\Command;

/**
* Interface Command
*
* Marker interface for CQRS write commands.
* Commands represent the intention to change the system state.
*
* @author Unay Santisteban <usantisteban@othercode.io>
*/
interface Command
{
}
18 changes: 18 additions & 0 deletions src/Application/Command/CommandBus.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace ComplexHeart\Application\Command;

/**
* Interface CommandBus
*
* @author Unay Santisteban <usantisteban@othercode.io>
*/
interface CommandBus
{
/**
* Dispatch the given command.
*/
public function dispatch(Command $command): void;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,19 @@

declare(strict_types=1);

namespace ComplexHeart\Domain\Contracts\ServiceBus;
namespace ComplexHeart\Application\Handler;

use ComplexHeart\Application\Command\Command;

/**
* Interface CommandHandler
*
* @author Unay Santisteban <usantisteban@othercode.es>
* @package ComplexHeart\Domain\Contracts\ServiceBus
* @author Unay Santisteban <usantisteban@othercode.io>
*/
interface CommandHandler
{
/**
* Handle the command execution.
*
* @param Command $command
*/
public function __invoke(Command $command): void;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,19 @@

declare(strict_types=1);

namespace ComplexHeart\Domain\Contracts\ServiceBus;
namespace ComplexHeart\Application\Handler;

use ComplexHeart\Domain\Events\Event;

/**
* Interface EventHandler
*
* @author Unay Santisteban <usantisteban@othercode.es>
* @package ComplexHeart\Domain\Contracts\ServiceBus
* @author Unay Santisteban <usantisteban@othercode.io>
*/
interface EventHandler
{
/**
* Handle the event execution.
*
* @param Event $event
*/
public function __invoke(Event $event): void;
}
21 changes: 21 additions & 0 deletions src/Application/Handler/QueryHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace ComplexHeart\Application\Handler;

use ComplexHeart\Application\Query\Query;
use ComplexHeart\Application\Query\QueryResponse;

/**
* Interface QueryHandler
*
* @author Unay Santisteban <usantisteban@othercode.io>
*/
interface QueryHandler
{
/**
* Handle the query execution.
*/
public function __invoke(Query $query): QueryResponse;
}
17 changes: 17 additions & 0 deletions src/Application/Query/Query.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace ComplexHeart\Application\Query;

/**
* Interface Query
*
* Marker interface for CQRS read queries.
* Queries represent the intention to read data without side effects.
*
* @author Unay Santisteban <usantisteban@othercode.io>
*/
interface Query
{
}
18 changes: 18 additions & 0 deletions src/Application/Query/QueryBus.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace ComplexHeart\Application\Query;

/**
* Interface QueryBus
*
* @author Unay Santisteban <usantisteban@othercode.io>
*/
interface QueryBus
{
/**
* Ask the given query.
*/
public function ask(Query $query): QueryResponse;
}
17 changes: 17 additions & 0 deletions src/Application/Query/QueryResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace ComplexHeart\Application\Query;

/**
* Interface QueryResponse
*
* Marker interface for query responses.
* Represents the data returned from a query execution.
*
* @author Unay Santisteban <usantisteban@othercode.io>
*/
interface QueryResponse
{
}
Loading