{"id":15529038,"url":"https://github.com/alexander-schranz/hexagonal-architecture-study","last_synced_at":"2026-03-04T02:02:46.212Z","repository":{"id":45456625,"uuid":"390849387","full_name":"alexander-schranz/hexagonal-architecture-study","owner":"alexander-schranz","description":"Project \"Rabbit Hole\": Dig into the rabbit hole of Hexagonal Architecture.","archived":false,"fork":false,"pushed_at":"2021-08-21T00:10:37.000Z","size":777,"stargazers_count":27,"open_issues_count":0,"forks_count":2,"subscribers_count":7,"default_branch":"main","last_synced_at":"2025-04-23T12:53:04.765Z","etag":null,"topics":["alexander-schranz-article","case-study","ddd","domain-driven-design","hexagonal-architecture","port-and-adapters","software-architecture"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alexander-schranz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-07-29T21:00:42.000Z","updated_at":"2025-04-15T19:53:41.000Z","dependencies_parsed_at":"2022-07-14T12:30:38.908Z","dependency_job_id":null,"html_url":"https://github.com/alexander-schranz/hexagonal-architecture-study","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/alexander-schranz/hexagonal-architecture-study","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexander-schranz%2Fhexagonal-architecture-study","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexander-schranz%2Fhexagonal-architecture-study/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexander-schranz%2Fhexagonal-architecture-study/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexander-schranz%2Fhexagonal-architecture-study/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alexander-schranz","download_url":"https://codeload.github.com/alexander-schranz/hexagonal-architecture-study/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexander-schranz%2Fhexagonal-architecture-study/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30069236,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-04T01:03:42.280Z","status":"online","status_checked_at":"2026-03-04T02:00:07.464Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["alexander-schranz-article","case-study","ddd","domain-driven-design","hexagonal-architecture","port-and-adapters","software-architecture"],"created_at":"2024-10-02T11:16:02.451Z","updated_at":"2026-03-04T02:02:46.198Z","avatar_url":"https://github.com/alexander-schranz.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Project Rabbit Hole\n\n\u003e Dig into the rabbit hole of Hexagonal Architecture.\n\n## Table of Contents\n\n - [1. Introducing](#1-introducing)\n - [2. Bounding Context](#2-bounding-context)\n - [3. Hexagonal Architecture](#3-hexagonal-architecture)\n   - [3.1 Domain Layer](#31-domain-layer)\n   - [3.2 Application Layer](#32-application-layer)\n   - [3.3 Infrastructure Layer](#33-infrastructure-layer)\n   - [3.4 UserInterface Layer](#34-userinterface-layer)\n - [4. Project Targets](#4-project-targets)\n - [5. Creating Domain Layer](#5-creating-domain-layer)\n   - [5.1 Modeling Domain Models](#51-modeling-domain-models)\n   - [5.2 Defining Domain Services](#52-defining-domain-services)\n - [6. Creating Application Layer](#6-creating-application-layer)\n   - [6.1 Creating Messages](#61-creating-messages)\n   - [6.2 Validating Messages](#62-validating-messages)\n   - [6.3 Creating Message Handlers](#63-creating-message-handlers)\n   - [6.4 Make Handlers extendable](#64-make-handlers-extendable)\n   - [6.5 Third party code inside application](#65-third-party-code-inside-applicatin)\n - [7 Testing Application Core](#7-testing-application-core)\n   - [7.1 Mocks vs no Mocks](#71-mocks-vs-no-mocks)\n   - [7.2 AAA Pattern](#72-aaa-pattern)\n - [8. Creating Infrastructure Layer](#8-creating-infrastructure-layer)\n   - [8.2 Creating Doctrine ORM Integration](#82-creating-doctrine-orm-integration)\n   - [8.3 Creating Cycle ORM Integration](#83-creating-cycle-orm-integration)\n   - [8.4 ORM Datamapper vs Active Record](#84-orm-datamapper-vs-active-record)\n   - [8.5 Creating Symfony Integration](#85-creating-symfony-integration)\n   - [8.6 Creating Spiral Integration](#86-creating-spiral-integration)\n - [9 Testing Infrastructure Layer](#9-testing-infrastructure-layer)\n   - [9.1 Create reusable test cases](#91-create-reusable-test-cases)\n   - [9.2 Create reusable test traits](#92-create-reusable-test-traits)\n - [10. Creating UserInterface Layer](#10-creating-userinterface-layer)\n   - [10.1 Create PSR 7 Controller](#101-create-psr-7-controller)\n - [11. Communicate between Bounded Contexts](#11-communicate-between-bounded-contexts)\n   - [11.1 Process Manager](#111-process-manager)\n - [12. Porting Application Core into another language](#12-porting-application-core-into-another-language)\n - [00. Summary](#00-summary)\n - [00.1 Thank you](#001-thank-you)\n - [00.2 Links](#002-links)\n\n## 1. Introducing\n\nThe \"Project Rabbit Hole\" is a case study by me - Alexander Schranz. I'm \ncurrently working as Web Developer for the Open Source Content Management \nSystem called [Sulu](https://github.com/sulu) and did begin my career as \ndeveloper in 2012. Today I'm mostly working with\n[Symfony](https://github.com/symfony/symfony),\n[Doctrime ORM](https://github.com/doctrine/orm),\n[Elasticsearch](https://github.com/elastic/elasticsearch),\n[Redis](https://github.com/redis/redis) and [ReactJS](https://reactjs.org).\n\nAt [Sulu](https://sulu.io) we did more and more use in our projects the \nHexagonal architecture. This case study should show the advantages of using \nthe Hexagonal architecture. How I'm interpreting it and how it does solve \nfor me the problem of creating long term maintainable software.\n\nThe case study focus on creating a reusable library which can be used in \ndifferent frameworks without the need of changing its core application logic.\nIt should also show where the weak points are and why some frameworks suit \nbetter to create sustainable software and why others not.\n\n## 2. Bounding Context\n\nBefore dig into the Hexagonal architecture I want to do a detour to Domain\nDriven Design (DDD). In there exist the term of Bounding Contexts which is a \nway to help to split your application into different contexts which should \nwork by their own.\n\nThe best example of how a software can be split into multiple context is a \nshop. A typical shop software can be split into the following context:\n\n - Product\n - Order\n - Invoice\n\nEvery context can work by itself. Example your website does only need to \npresent products on pages. In this case there is no order or invoice needed.\nSo the product is defined as its own bounded context. In other case the \norder context can exist without invoice because invoicing is done over a \nthird party service.\n\nIn this Contexts the Order does just provide an API / Interfaces to create \nOrder Items, but does know nothing about the Product. In this type of \ncontext setup it's also easier to add additional thing which can be ordered, in \nexample an own Ticket context.\n\nThe contexts a shop would then look like this:\n\n```\n+---------+\n| Product |-------+      \n+---------+       |       +-------+       +---------+\n                  +-------+ Order |-------+ Invoice |\n+---------+       |       +-------|       +---------+\n| Ticket  |-------+\n+---------+\n```\n\nWe will go deeper into how the contexts can communicate / integrate in the \nchapters about the Hexagonal architecture.\n\nThe hardest thing in the initial project setup is defining the contexts, \nwhat does belong into which context. Having to many contexts can make the \nsoftware hard to maintain and make it hard to understand how things work \ntogether. Having to less can make the code also messy and rewrites of \nspecific contexts hard or even impossible in certain time.\n\nAbout bounded context and DDD have also a look at Martin Fowlers website: \n[https://martinfowler.com/tags/domain%20driven%20design.html](https://martinfowler.com/tags/domain%20driven%20design.html)\nabout DDD topics.\n\nIf creating a library mostly your library is the whole context. In the example\ncode the context is called Event and will implement a simple translatable\nEvent model which can be created, modified, loaded and removed.\n\n## 3. Hexagonal architecture\n\nThe [\"Hexagonal architecture\"](https://en.wikipedia.org/wiki/Hexagonal_architecture_(software))\nalso called \"Ports and Adapters\", is a way of how you can split your \ncontext/library into a maintainable and reusable way.\n\nIf you are interested in the origin of the hexagonal architecture you should\ndefinitely have a look at the Alistair Cockburn first published article\nabout it on his website. Sadly currently only available in the webarchive\n[https://web.archive.org/web/20170730135337/http://alistair.cockburn.us/Hexagonal+architecture](https://web.archive.org/web/20170730135337/http://alistair.cockburn.us/Hexagonal+architecture).\n\nMost which I'm familiar with is coming from the great Matthias Noback which\ndid write several articles about this topic which can be found on his website\n[https://matthiasnoback.nl/tags/hexagonal%20architecture/](https://matthiasnoback.nl/tags/hexagonal%20architecture/).\n\nThe architecture is mostly shown the following way:\n\n```\n+--------------------------------------------------------+\n|                                                        |\n|                         Adapters                       |\n|                                                        |\n|          +----------------------------------+          |\n|          |                                  |          |\n|          |               Ports              |          |\n|          |                                  |          |\n|          |       +------------------+       |          |\n|          |       |                  |       |          |\n| Adapters | Ports | Application Core | Ports | Adapters |\n|          |       |                  |       |          |\n|          |       +------------------+       |          |\n|          |                                  |          |\n|          |               Ports              |          |\n|          |                                  |          |\n|          +----------------------------------+          |\n|                                                        |\n|                         Adapters                       |\n|                                                        |\n+--------------------------------------------------------+\n```\n\nHere we already see why it is called \"Ports \u0026 Adapters\" the Application core \ncommunicates over ports with its adapters.\n\nWe will use a common Layered Hexagonal Architecture in our code. The main \nbusiness logic should in this architecture exist in the `Application Core`,\nwhich we will split into the `Domain` and the `Application` namespace. The \nadapters we will split into `UserInterface` and `Infrastructure`. This layers \nare also common without the UserInterface so the UserInterface adapters there \nlive in the Infrastructure layer. The ports to the adapters should be kept that\nthe adapters are easily replaceable or new adapters can be added:\n\n```\n|       Adapters       | Port |                  Application Core               | Port |           Adapters           |\n+----------------------+------+-------------------------------------------------+------+------------------------------+\n|                      |      |                                                 |      |                              |\n\n\n                                                           +--------------------+\n                                                           |       Domain       |\n                    +-------------------------------------\u003e|                    |      +-----------------------------+\n                    |                                      |   +------------+   |      |        Infrastructure       |\n +---------------------+      +------------------------+   |   |   Model    |   |      |                             |\n |    UserInterface    |      |      Application       |--\u003e|   +------------+   |      |   +---------------------+   |\n |                     |      |                        |   |                    |\u003c-----|   | Doctrine Repository |   |\n |   +------------+    |      |   +----------------+   |   |   +------------+   |      |   +---------------------+   |\n |   | Controller |    |-----\u003e|   |     Message    |   |--\u003e|   | Repository |   |      |                             |\n |   +------------+    |      |   +----------------+   |   |   +------------+   |      |   +---------------------+   |\n |                     |      |                        |   |                    |\u003c-----|   |     Serializer      |   |\n |   +------------+    |      |   +----------------+   |   |   +------------+   |      |   +---------------------+   |\n |   |   Command  |    |-----\u003e|   | MessageHandler |   |--\u003e|   | Exception  |   |      |                             |\n |   +------------+    |      |   +----------------+   |   |   +------------+   |      |   +---------------------+   |\n +---------------------+      +------------------------+   |                    |\u003c-----|   |     Message Bus     |   |\n           |                              A                |   +------------+   |      |   +---------------------+   |\n           |                              |                |   |    Event   |   |      +-----------------------------+\n           |                              |                |   +------------+   |             |        A\n           |                              |                +--------------------+             |        |\n           |                              |                                                   |        |\n           |                              +---------------------------------------------------+        |\n           |                                                                                           |\n           +-------------------------------------------------------------------------------------------+\n```\n\nThe Application Core, which includes the `Domain` and `Application` namespaces,\nshould have no external dependency when adopting a strict Hexagonal\nArchitecture.\n\nThe Adapters, which live in the `UserInterface` and `Infrastructure` namespaces,\nwhich are very specific like the Repository implemented with \nan ORM like Doctrine lives in the Infrastructure namespace and are \nreferenced in the Application Core just by a RepositoryInterface and by the \n`Infrastructure` Code for Symfony Framework correctly injected over \nDependency Injection.\n\nSo now we already did talk about the 4 Layers of our Hexagonal Architecture \nwhich are:\n\n - Application\n - Domain\n - Infrastructure\n - UserInterface\n\nThe Hexagonal Architecture defines a specific way which layers are allowed \nto access the models and services of other layers. This is shown in the \nprevious graphic with the arrows.\n\n### 3.1. Domain Layer\n\nThe domain layer is the layer which contains your domain logic and is only \nallowed to access classes and interface of itself.\nIt provides mostly the following:\n\n - `Events`\n - `Exceptions`\n - `Factories`\n - `Models`\n - `Repository`\n\nThe models should be defined as rich models, so they should contain the logic \nfor your domains, thrown expected exception for the Application or \nUserInterface. As Port to other Layers the Domain layer should provide for \nexample an Interface for the Persistence Layer over a \nRepositoryInterface. The Interface can be used then by an Infrastructure \nImplementation. This type of pattern is well known as the \"Dependency \nInversion Principle\" (DIP).\n\n### 3.2. Application Layer\n\nThe application layer defines the services and application models. It is \nallowed to access the [Domain Layer](#31-domain-layer) and itself. In our case\nmostly the following exist in the Application Layer:\n\n - `Messages`\n - `MessageHandlers`\n - any other application service provided by your library/project\n\nActually the Messages and MessageHandlers are in our Setup Commands and \nCommandHandlers from CommandBus. But as command are in most framework \nassociated with CLI scripts we did decide to go here with Message and \nMessageHandler how it is done in the Symfony Framework.\n\nIn real-world applications you will find yourself quickly in\na place of reinventing the wheel. That’s the reason why we allow the usage of\nsome external dependency like PSR EventDispatcher or MessageBusInterface in the\nApplication namespace and Ramsey Uuid in our Domain Models. See also \n[Project Targets](#4-project-targets).\n\n### 3.3. Infrastructure Layer\n\nThe infrastructure layer defines your adapters to other libraries like ORMs \nor to integration of your code into frameworks. The infrastructure is \nallowed to access the [Domain Layer](#31-domain-layer) and the\n[Application Layer](#32-application-layer). In our case mostly the following\nexist in the Application Layer:\n\n - `Framework` Integrations\n   - Dependency Injection\n - `ORM` Integrations\n   - Repository\n\nAs we need to keep the Domain and Application free from Infrastructure code \nthings like ORM annotations should be replaced with xml files or defining \nthe ORM schema over external code files.\n\nThe Hexagonal Architecture targets that such things like an ORM can in future\nbe easy replaceable. As an example replacing Doctrine with Cycle ORM should\nhave no impact on your Business Logic and should so easily be achievable with\nthis architecture. Or another example the service are registered in Symfony \ncorrectly providing in the Symfony Infrastructure Namespace a Bundle class \nor for the Spiral framework providing a Bootloader class.\n\n### 3.4. UserInterface Layer\n\nThe userinterface layer as it says is where user input is coming in. The \nuserinterface is allowed to access all other layers so the \n[Domain Layer](#31-domain-layer), [Application Layer](#32-application-layer)\nand [Infrastructure Layer](#33-infrastructure-layer). The UserInterface \nlayer contains in our setup:\n\n - Controllers\n - Commands (CLI Scripts)\n\nDifferent frameworks need that there controllers or commands are written in \na certain way. See also [Project Targets](#4-project-targets).\n\n## 4. Project Targets\n\nThe first target of the Project \"Rabbit Hole\" is to give other developers a\ngreat overview about Hexagonal architecture. This should not only be done by \nthis text but also by a whole implemented library which code the reader can\nanalyse. So the developers are not getting lost in the \"Rabbit Hole\" of\nHexagonal architecture.\n\nThe second target and personally target is finding a balance between a strict\nHexagonal architecture and avoiding reinventing the wheel. This should be \ndone by build on top of widely spread interfaces providing the \n[PSR packages](https://www.php-fig.org/psr/) and other well spread library. \nAs example here [ramsey/uuid](https://github.com/ramsey/uuid) and\n[flysystem](https://github.com/thephpleague/flysystem). I think this balance \nis very important to keep the development productive and that the \narchitecture is not a stopper and so drawing the line must be defined for each\nindividual service.\n\nThe third target of this implementation is to show which frameworks make it \nharder to implement its infrastructure / userinterface layers and requires more \nwork and which can easily. Why some patterns don't work and why others are \nbetter suited for such libraries.\n\nThe fourth target is can user interfaces defined which can be used by \ndifferent frameworks, so does a framework change maybe also don't need to \nrequire that your libraries user interfaces also don't need to be rewritten. \nAnd so a library can easily support multiple frameworks and provide so its \nmodel and entities for it.\n\nThe project is for maintainable creating in a monorepository, the library \nwill exists in the [src/event](src/event) directory with its Hexagonal \nArchitecture. \nThe proof that the library can be used in different framework is done by \ninstalling different framework skeletons into the [frameworks](frameworks) \ndirectory which will over local composer package install the library and use\nits framework integration.\n\nAfter this introduction in the theoretical part it is time to go into the \npractical part.\n\n## 5. Creating Domain Layer\n\nThe domain namespace is responsible for the domain models and services. \nCommon things inside it are Models, Exceptions, Events, Repository (Port), \nFactories.\n\n### 5.1. Modeling Domain Models\n\nThe best way of defining the Domain Models is to begin with some kind of class \ndiagram. This allows you to discuss relations between your models without \nthe need of actually implementing them. In our example we have a very basic \nmodels with an `Event` and an `EventTranslation`. Modeling the Domain classes \nshould help you also define the Bounded Contexts which from\n[Chapter 2 - Bounding Context](#2-bounding-context).\n\n```\n+-----------------------------------[ Event Context ]---------------------------------+\n|                                                                                     |\n|     +---------------------------------+               +-----------------------+     |\n|     |           Event                 |               |  EventTranslation     |     |\n|     +---------------------------------+               +-----------------------+     |\n|     | -id: int                        | 1           n | -id: int              |     | \n|     | -defaultLocale: string          |\u003c-------------\u003e| -locale: string       |     |\n|     +---------------------------------+               | -title: string        |     |\n|     | + getDefaultLocale(): string    |               +-----------------------+     |\n|     | + getTranslations(): i\u003cET\u003e      |               | + getTitle(): string  |     |\n|     | ...                             |               | ...                   |     |\n|     +---------------------------------+               +-----------------------+     |\n|                                                                                     |\n+-------------------------------------------------------------------------------------+\n```\n\nIn which detail you creating this diagrams is always up to you. Now as we \nhave defined our Models we can begin to implement them.\n\n - [Event.php](./src/event/src/Domain/Model/Event.php)\n - [EventTranslation.php](./src/event/src/Domain/Model/EventTranslation.php)\n\nAfter this we extract the Model public methods into an Interface mostly IDEs \nare helping here like Intellij/PHPStorm from Jetbrains. After that we should \nuse everywhere where we are expecting a specific Model using the Interface \ninstead of the Model class.\n\n - [EventInterface.php](./src/event/src/Domain/Model/EventInterface.php)\n - [EventTranslationInterface.php](./src/event/src/Domain/Model/EventTranslationInterface.php)\n\nIn the next step we should also define in our Domain which Exception could \nhappen. At the begin of a project there will be not much Exception so we \nhave here 2 expected Exceptions:\n\n- [EventNotFoundException.php](./src/event/src/Domain/Exception/EventNotFoundException.php)\n- [EventTranslationNotFoundException.php](./src/event/src/Domain/Model/EventTranslationNotFoundException.php)\n\nThe Domain Models should not be just some Entities with getter and setter \nthey should implement also some kind of business logic. Mostly here is \nspoken about Rich Models, which can have complex logic in itself and also throw\nexceptions when something unexpected happens.\n\nHere a more complex example on a User Model:\n\n```php\nclass User {\n    private string $username;\n    private string $email;\n    private string $passwordHash;\n\n    public function __construct(\n        string $username,\n        string $email,\n        string $rawPassword,\n        callable $passwordHasher\n    ) {\n        $this-\u003echangeUsernameAndEmail($username, $email);\n        $this-\u003echangePassword($rawPassword, $passwordHasher);\n    }\n\n    public function changeUsernameAndEmail(string $username, string $email): void\n    {\n        if (str_contains($username, '@') \u0026\u0026 $username !== $email) {\n            throw new \\InvalidArgumentException('A username containing the @ symbol must much the given email address.');\n        }\n\n        $this-\u003eusername = $username;\n        $this-\u003eemail = $email;\n    }\n\n    public function changePassword($rawPassword, callable $passwordHasher): void\n    {\n        $this-\u003epasswordHash = $passwordHasher($rawPassword);\n    }\n}\n```\n\nThe advantage of rich models are also that they are valid objects after they \nare constructed. So the constructor of your model already defines all required \nattributes which the Model needs to be persisted and setters are only used \nfor optional fields. Also it is common that not every field has a setter and \nsome can so only be set at the beginning of creating the model, when example \na field is not changeable like our `EventTranslation::locale`.\n\n### 5.2. Defining Domain Services\n\nThe most common service which exist in the Domain is the ModelRepository. As \nthe Repository should allow to save our Models in a persistence storage we \nneed to create a Port for it. This Port is the RepositoryInterface which \nuses Dependency Inversion Principle so there is no direct relation to the \npersistence storage.\n\nAnother common service is the Domain is the ModelFactory to create an \ninstance of your model. In our case there exist no ModelFactory as we did \ndirectly add a create and createTranslation method to our Repository. This is \nbecause of experience that how a model need to be constructed can depend on the\nused persistence layer.\n\n - [EventRepositoryInterface.php](./src/event/src/Domain/Repository/EventRepositoryInterface.php)\n\nThe Interface will make use of our before defined EventNotFoundException in \ncase of the `getOneBy` is called with none exist filter parameters. If \ncarefully look at the defined RepositoryInterface you will see that there \nare 2 methods which look equal the `getOneBy` and `findOneBy`. Where in most \ncases the `getOneBy` where the Model is returned or a EventNotFoundException \nis thrown is the best. There are usecases where the `findOneBy` make more \nsense to make readable code. As an example: \n\n```php\n// bad example for using getOneBy\ntry {\n   $existUser = $this-\u003euserRepository-\u003egetOneBy(['username' =\u003e $username]);\n   \n   throw new UsernameAlreadyTakenException($username);\n} catch (UserNotFoundException $e) {\n   // expected program flow\n}\n\n$user = new User($username);\n\n// ..\n```\n\nIn this case the `findOneBy` is a better used the expected program flow is \nnot going through an exception:\n\n```php\n// good example replacing getOneBy with findOneBy\n$existUser = $this-\u003euserRepository-\u003efindOneBy(['username' =\u003e $username]);\n\nif ($existUser) {\n    throw new UsernameAlreadyTakenException($username);\n}\n\n$user = new User($username);\n\n// ..\n```\n\nHow a RepositoryInterface can be defined is an\n[own topic](https://github.com/sulu/sulu/issues/5931). I personally \nprefer that the Interfaces look similar to each other instead of defining too \nspecific methods. Still I would not create a common interface as it should \njust define the method you really require and inheritance always adds \ncomplexity.\n\nWith the created RepositoryInterface we did define our minimal domain setup.\n\n## 6. Creating Application Layer\n\nThe minimal domain setup in the chapter before allows us now to create our \napplication logic. The application layer provides the services for the \noutside which can be used. In our case we have decided that we want to use a \ncommand bus to communicate with our application. Because of conflicts of the \nname command with CLI command/scripts in widely used frameworks we have \ndecided to go here instead with Command and CommandHandler with Message and \nMessageHandler.\n\n### 6.1. Creating Messages\n\nThe messages or commands are at the end Data transfer objects (DTOs), which \nconverts the given data into an accessible format for the handlers:\n\n - [CreateEventMessage.php](./src/event/src/Application/Message/CreateEventMessage.php)\n - [ModifyEventMessage.php](./src/event/src/Application/Message/ModifyEventMessage.php)\n - [RemoveEventMessage.php](./src/event/src/Application/Message/RemoveEventMessage.php)\n\nAll data should be injected into the message over the constructor and so the \nmessage should be immutable by design. It should only define getter methods to \nmake data easier to access. The data given to the message should just use \nprimitives types, this allows to create it from different contexts\n(cli/http/...). Also no data should be needed to be parsed out like in \narguments of a cli command. This type of things should happen in the \nUserInterface layer.\n\nThe primitives types not only allows to create the message at any case but \nalso make it easier when using like in our case a $data array directly \nforwarding in example our $_POST data or in whatevery case your framework \ndoes provide this kind of data. Example in symfony with the http foundation \nrequest data:\n\n```php\n$message = new CreateEventMessage($request-\u003erequest-\u003eall());\n```\n\nWhere we did in our case created typical create, modify and remove messages. \nIt is common that there exist more specific message which represent your \ndomain. Example here could be a `ChangePasswordMessage` or a \n`PublishEventMessage`.\n\n### 6.2. Validating Messages\n\nThe messages can be validated in different ways. It's possible to validate it \ndirectly in the constructor. If an unexpected data is given you can throw a \nInvalidArgumentException e.g.:\n\n```php\nAssert::string($data['title'] ?? null, 'Expected a valid title to be given.');\n```\n\nThis is one of the simplest and easiest way for validation. Another way of \nvalidating the inputted data is adding a validating method to it. e.g.:\n\n```php\npublic function validate(): void\n{\n    $errors = [];\n    \n    if (empty($this-\u003edata['title']) || !is_string($this-\u003edata['title'])) {\n        $errors[] = [\n            'field' =\u003e 'title',\n            'message' =\u003e 'Expected a valid title to be given.',\n        ];\n    }\n    \n    if (empty($this-\u003edata['locale'])\n        || !is_string($this-\u003edata['locale']) ||\n        !in_array($this-\u003edata['locale'], ['en', 'de'])\n     ) {\n        $errors[] = [\n            'field' =\u003e 'locale',\n            'message' =\u003e 'Expected locale \"en\" or \"de\" given.',\n        ];\n    }\n    \n    throw new ValidationException($errors);\n}\n```\n\nThe advantage of this method is that you validate multiple inputted data and \nnot just one by one. Instead of an exception you can also go with a boolean \nreturn:\n\n```php\nprivate ?bool $valid = null;\nprivate array $errors = [];\n\npublic function valid(): bool\n{\n    // if the message is already validated we don't need to validate it again\n    if ($this-\u003evalid !== null) {\n        return $this-\u003evalid;\n    }\n\n    if (empty($this-\u003edata['title']) || !is_string($this-\u003edata['title'])) {\n        $this-\u003eerrors[] = [\n            'field' =\u003e 'title',\n            'message' =\u003e 'Expected a valid title to be given.',\n        ];\n    }\n\n    if (empty($this-\u003edata['locale'])\n        || !is_string($this-\u003edata['locale']) ||\n        !in_array($this-\u003edata['locale'], ['en', 'de'])\n     ) {\n        $this-\u003eerrors[] = [\n            'field' =\u003e 'locale',\n            'message' =\u003e 'Expected locale \"en\" or \"de\" given.',\n        ];\n    }\n \n    $this-\u003evalid = empty($this-\u003eerrors);\n \n    return $this-\u003evalid;\n}\n\npublic function getErrors(): array\n{\n    if (null === $this-\u003evalid) {\n        throw new \\LogicException('Run \"valid\" before calling \"getErrors\"');\n    }\n\n    return $this-\u003eerrors;\n}\n```\n\nAll of the above methods I think are good ways for validations, another way \nwould be using an own service which is validating the message. This \nspecially make sense if the message can be extended with additional data \nwhich need to be validated.\n\n```php\n$message = new CreateEventMessage($data);\n$validator-\u003evalidate($message);\n```\n\nSo there are different ways of validating of the message, you also always \nneed to ask how much you want to validate inside your application core. In most \ncases I would go with the first solution by validating it directly in the \nconstructor. If your controller would need another type of validation to \nshow better error messages you could provide some json schema standard. \nThis type of standard allows you that you can not only validate it in a \ncontroller but also the client could validate it before triggering your api. \n\nWhere I personally prefer the pattern with to give an whole array into the \nmessage, it is very common that you design your message / command constructor \nwith the data which is required:\n\n```php\n$message = new CreateEventMessage($locale, $title);\n```\n\nWhich advantage of disadvantage this have we will see in the\n[Make Handlers extendable](#64-make-handlers-extendable) chapter.\n\n### 6.3. Creating Message Handlers\n\nThe message handlers implements what will happen when the message is \ndispatch. In our case they are responsible for creating, modifying and \nremoving an entity.\n\nAs mention already in the [creating messages chapter](#61-creating-messages) \nits common that there are also more specific messages like \n`ChangePasswordMessage` or `PublishEventMessage`, which are modifying our \nobjects not in the typical crud way.\n\nFor our typical handlers which we created the handler need the Repository. The \nrepository of our models is represented in our Handlers only be the\nRepositoryInterface, which make over the Dependency Inversion Principle\npossible that different implementation or changeable implementation could be\nbehind it.\n\nWith the message we did already keep our handlers clean from request objects, \nsessions and other things which are only needed to receive or deliver \nsomething.\n\nAlso with the message object we already did make sure the data we are \nreceiving are already validated. So we can here in our create and modify \nhandler load the requested model and call the mappers to map directly the data \nto the model.\n\n- [CreateEventMessageHandler.php](./src/event/src/Application/MessageHandler/CreateEventMessageHandler.php)\n- [ModifyEventMessageHandler.php](./src/event/src/Application/MessageHandler/ModifyEventMessageHandler.php)\n- [RemoveEventMessageHandler.php](./src/event/src/Application/MessageHandler/RemoveEventMessageHandler.php)\n\nIn the case of the Modify and Remove handler the identifier is represented \nby an array (Map). The decision here was to go instead of using `int $id` to \ngo with an `array $identifier` to make it possible to modify or remove our \nmodel not only by its id but also by any other unique identifier a model \ncould have. For example an uuid or an unique key field.\n\nThe handlers are in our case returning nothing or returning the object. They \nare not responsible how the object will be represented, this should not be \npart of them. The handlers can throw events which could trigger other \nprocesses like sending an email. Also other bounded contexts could listening \nto this events to update there data with the done changes.\n\n### 6.4. Make Handlers extendable\n\nThe datamapper which we added to the CreateEventMessageHandler and \nModifyEventMessageHandler make it possible that we don't need to duplicate \nthe code to map data to our Model which are the same between created and \nmodify.\n\n- [EventDataMapper.php](./src/event/src/Application/MessageHandler/CreateEventMessageHandler.php)\n\nBut this is not the only advantage of it. If we are allowing multiple \nmappers we can make our create and modify handlers extendable. By \nintroducing an interface for the mapping:\n\n- [DataMapperInterface.php](./src/event/src/Application/Datamapper/EventDataMapperInterface.php)\n\nAlso have here a look at the so called [Visitor Pattern](https://en.wikipedia.org/wiki/Visitor_pattern)\nwhich make this type of things possible.\n\nThe requirement for making it extendable was that our messages allows that \nadditional data can be provided to our handler. This is done over the \nintroduced getData method.\n\n### 6.5 Third party code inside application\n\nWhere it is good to keep your application core small of dependency it doesn't \nmean that all your vendor code is automatically infrastructure code.\n\nThe connection to infrastructure code is mostly done over an Interface using \nthe Dependency Inversion Principle. PHP provides over\n[PSR](https://www.php-fig.org/psr/) many common interfaces where no own \ninterface need to be written.\n\nAt the time I'm written this chapter Matthias Noback has released an article \nabout this topic which is worse to read:\n[https://matthiasnoback.nl/2021/08/on-using-psr-abstractions/](https://matthiasnoback.nl/2021/08/on-using-psr-abstractions/).\n\nThere are other common libraries which are for example abstractions \nlike [Flysystem library](https://github.com/thephpleague/flysystem) for local\nand remote filesystems access by\n[Frank de Jonge](https://github.com/frankdejonge).\nThis type of libraries already provides interfaces you can use, mostly this \ninterfaces are more bulletproof than if we would write our own. Also this \nkind of libraries can be inspiring how you can use the \n[Adapter pattern](https://flysystem.thephpleague.com/v2/docs/architecture/) to \nconnect to your infrastructure code.\n\nAlso small libraries do not hurt to add to your application code, it should \nnot have the need to reinvent the wheel, common libraries are example the \n[webmozart/assert](https://github.com/webmozarts/assert) which provides you \na good way to valid the data given to the message constructor. Or a small \nlibrary like the [ramsey/uuid](https://github.com/ramsey/uuid) to generate \nuuid4 strings for your application.\n\nThings like how your code or library is integrated into a framework or how it \nis communicating with the database, orm should still definitely live in the \ninfrastructure code.\n\n## 7. Testing Application Core\n\nA clean architecture, following the concepts of\n[SOLID](https://en.wikipedia.org/wiki/SOLID) software engineering, specially \n[single responsibility](https://en.wikipedia.org/wiki/Single-responsibility_principle)\nshould make your code easier to test with the framework you want.\n\n### 7.1. Mocks vs no Mocks\n\nAs there are Interfaces used to connect to your Infrastructure code, you \nwill naturally create mocks for them. Where in somecases easier, you need to\nknow about the inner structure of the class / method you want to test. This is\nsomething you want to avoid. The inner part of a method should be changable and\naslong as you don't change input or output of that method the tests should not \nbe needed to be changed.\n\nIn case of the RepositoryInterface we created in the Domain the solution is \nthat we provide a InMemoryRepository. This InMemoryRepository can also be a \nsmall reference how our repository methods should behave and return things \nbeside the interface definition.\n\nThings like PSR 3 for logs already provides functional working \nimplementation like the NullLogger which can also be used in the tests.\n\nIf testing without mocks is used you maybe need inside your tests use an \ninstance of your infrastructure code. As an example using the symfony event \ndispatcher as a psr 14 implementation. So there is no need to create here \nyour own implementation of an interface only for testing.\n\nI can recommend to give testing without mocks a try, atleast I would \navoid mocking things like models, message in your tests. Creating an \ninstance of them and using them directly does make your tests a lot more \nmaintainable and avoid that an inner refractoring of a service requires to \nrewrite all your tests. More about this topic you can find on\n[Frank de Jonge](https://github.com/frankdejonge) blog in the\n[https://blog.frankdejonge.nl/testing-without-mocking-frameworks/](https://blog.frankdejonge.nl/testing-without-mocking-frameworks/).\narticle.\n\n### 7.2. AAA-pattern\n\nThe [AAA pattern](https://medium.com/@pjbgf/title-testing-code-ocd-and-the-aaa-pattern-df453975ab80)\nis a way how to write tests, in most cases you are doing this kind of things \nnaturally. But knowing that there exist this pattern help you to avoid \nvioleting it.\n\nAAA stands for Arrange, Act, Assert. Which are the 3 steps you should write \nyour test.\n\nFirst you arrange all you need to test the method in our case for a test \nwith the handler, the handler itself with the required repository and the \nmessage we want to test:\n\n```php\n// Arrange\n$repository = new InMemoryEventRepository();\n$handler = new CreateEventMessageHandler($repository);\n\n$message = new CreateEventMessage(['locale' =\u003e 'en', 'title' =\u003e 'Test']);\n```\n\nAfter you arranged all you need you actually call what you want to test in \nthe step of Act:\n\n```php\n// Act\n$event = $handler-\u003e__invoke($message);\n```\n\nAfter this we finish the test with asserting the expected result.\n\n```php\n// Assert\n$translation = $event-\u003efindTranslation('en');\n$this-\u003eassertNotNull($translation);\n$this-\u003eassertSame('en', $translation-\u003egetLocale());\n$this-\u003eassertSame('Test', $translation-\u003egetTitle());\n```\n\nIf doing this only once per test, the tests are very readable.\nIn combination with mock-free testing this makes the application testing code\nreadable and maintainable.\n\n## 8. Creating Infrastructure Layer\n\nThe infrastructure provides implementation for different Interfaces which are \naccessed in our application code. The interface should make the \nInfrastructure replaceable and our application core should work without it. In \nthe following examples it is shown how to implement different ORMs and how to\nintegrate our application code into different frameworks.\n\n### 8.2. Creating Doctrine ORM Integration\n\nComing soon...\n\n### 8.3. Creating Cycle ORM Integration\n\nComing soon...\n\n### 8.4. ORM Datamapper vs Active Record\n\nComing soon...\n\n### 8.5. Creating Symfony Integration\n\nComing soon...\n\n### 8.6. Creating Spiral Integration\n\nComing soon...\n\n## 9. Testing Infrastructure Layer\n\nComing soon...\n\n### 9.1. Create reusable test cases\n\nComing soon...\n\n### 9.2. Create reusable test traits\n\nComing soon...\n\n## 10. Creating UserInterface Layer\n\nComing soon...\n\n### 10.1. Create PSR 7 Controller\n\nComing soon...\n\n## 11. Communicate between Bounded Contexts\n\nComing soon...\n\n### 11.1 Process Manager\n\nComing soon...\n   \n## 11. Porting Application Core into another language\n\nComing soon...\n\n## 00. Summary\n\nComing soon ...\n\n - Hexagonal Architecture\n   - Layers (clear structure)\n   - Dependency Inversion Principle (connection between layers)\n   - Adapter Pattern (connection to infrastructure)\n   - Visitor Pattern (extendability)\n ...\n\n## 00.1 Thank you\n\nAt first, I want to thank here all my colleagues from Sulu without the I would\nnot be able to write this and all feedback they gave me. Specially here Thomas\nSchedler which always pushing us to look \"beyond the tellerand\" ;). I also want\nto thank here the spiral community which did help me a lot to integrate the Cycle\nORM integration as an alternative persistence layer. A lot of things which I\nlearned about Hexagonal architecture is coming from Matthias Noback blogs about\nthis topic. So I also want to thank him here to share his knowledge with us, \nthat we can create sustainable software. Another man which was also an \ninspiration for me is Frank do Jonge, creator of flysystem and enthusiast of \ntesting without mocking, which did show me a way of making tests more \nmaintainable.\n\nAnd at the end I want to thank you, the reader of this, thank you for taking\nthis time I hope you did find something useful in my \"Project Rabbit Hole\".\n\n## 00.2 Links\n\n - https://web.archive.org/web/20170730135337/http://alistair.cockburn.us/Hexagonal+architecture\n - https://martinfowler.com/tags/domain%20driven%20design.html\n - https://en.wikipedia.org/wiki/Hexagonal_architecture_(software)\n - https://en.wikipedia.org/wiki/Visitor_pattern\n - https://en.wikipedia.org/wiki/SOLID\n - https://www.php-fig.org/psr/\n - https://en.wikipedia.org/wiki/Single-responsibility_principle\n - https://flysystem.thephpleague.com/v2/docs/architecture/\n - https://blog.frankdejonge.nl/testing-without-mocking-frameworks/\n - https://medium.com/@pjbgf/title-testing-code-ocd-and-the-aaa-pattern-df453975ab80\n - https://matthiasnoback.nl/tags/hexagonal%20architecture/\n - https://matthiasnoback.nl/2021/08/on-using-psr-abstractions/\n - https://matthiasnoback.nl/2020/02/is-all-code-in-vendor-infrastructure-code/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexander-schranz%2Fhexagonal-architecture-study","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falexander-schranz%2Fhexagonal-architecture-study","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexander-schranz%2Fhexagonal-architecture-study/lists"}