Domain-Driven Design in PHP
Domain-Driven Design in PHP
Buy on Leanpub

Foreword by Matthias Noback

I must admit that when I first heard of the Domain-Driven Design in PHP initiative, I was a bit worried. The danger was twofold: first of all, when glancing over the table of contents, the subject matter looked like it was a rehash of content that was already available in several other Domain-Driven Design books. Second, writing a book on Domain-Driven Design targeted specifically toward the PHP community seemed needlessly narrowing, particularly as Domain-Driven Design itself is not language specific. As such, this might inhibit PHP developers from looking past the boundaries of their own community, especially when considering that there’s a lot going on beyond the scope of PHP. In fact, even Domain-Driven Design is one of those things, as it didn’t originate in the PHP community.

After reading the book, I’m happy to inform you that my worries have been invalidated!

With regard to my first concern: of course there is some overlap with previously published Domain-Driven Design books. Yet the authors have restrained themselves. The theoretical parts are exactly what you need to be able to understand what’s going on in the code samples. Besides, if you never read another Domain-Driven Design book, this one gives you what you need to start applying some Domain-Driven Design principles and patterns in your code, as it’s practical by nature.

My second concern — about the PHP aspect of this book — has been addressed very well. It turns out there are a lot of things to say about Domain-Driven Design in a PHP world. This book is specifically targeted at an audience consisting of PHP developers. The code samples resemble real-world PHP projects, and the authors use a programming style we know from projects using Symfony or Silex. For persisting Domain objects, Doctrine ORM — which is the de facto standard data mapper for PHP — is used.

This book also fulfills a need I’ve often seen in the PHP community: the need for concrete examples. It’s not always easy for authors to come up with proper illustrations of how to apply certain ideas that have a low risk of being misinterpreted or abused in real-world projects. And in Domain-Driven Design, which is philosophical by nature, this is even more challenging.

In the case of this book, the authors haven’t been afraid to show many useful examples, along with some interesting alternative solutions. They aren’t just handwaving at solutions either; they take the time to provide detailed explanations — such as when they talk about saving snapshots for Aggregates with a large number of Domain Events, or when they discuss integrating Bounded Contexts using RabbitMQ. I can’t recall having previously seen an implementation of these things in a book or article on Domain-Driven Design.

For me personally, Domain-Driven Design is one the most interesting subjects in software development today. There is so much to discover, and there are many subjects related to it: Agile software development, TDD, and BDD, but also living documentation, visualization, and knowledge crunching techniques. Once you start looking into all of this, you’ll realize that Domain-Driven Design is an area of expertise worth investigating, as it enables you to add much more to your own worth as a software developer.

So, I guess what I want to say is this: dive into this book, learn from it, and then pick up another book (see the list of references at the end of this book for suggestions of future reading). Continuous learning is a fundamental part of keeping up to date in the software industry, so don’t stop here.

Oh, and by the way: if you get a chance to go to Barcelona, make sure you take part in one of the many PHP or Symfony events. The community is big, friendly, and full of interesting ideas. You’ll find the authors of this book there too. They are all invested in the local PHP community and are happy to share their insights and experiences with you!

Matthias Noback
Author of A Year with Symfony

Preface

In 2014, after two years of reading about and working with Domain-Driven Design, Carlos and Christian, friends and workmates, traveled to Berlin to participate in Vaughn Vernon’s Implementing Domain-Driven Design Workshop. The training was fantastic, and all the concepts that were swirling around in their minds prior to the trip suddenly became very real. However, they were the only two PHP developers in a room full of Java and .NET developers.

Around the same time, php[tek], an annual PHP conference, opened its call for papers, and Carlos sent one about Hexagonal Architecture. His talk was rejected, but Eli White — of musketeers.me and php[architect] fame — got in touch with him a month later wondering if he was interested in writing an article about Hexagonal Architecture for the magazine php[architect]. So in June 2014, Hexagonal Architecture with PHP was published. That article, which you’ll find in the appendix, was the origin of this book.

In late 2014, Carlos and Christian talked about extending the article and sharing all their knowledge of and experience in applying Domain-Driven Design in production. They were very excited about the idea behind the book: helping the PHP community delve into Domain-Driven Design from a practical approach. At that time, concepts such as Rich Domain Models and framework-agnostic applications weren’t so common in the PHP community. So in December 2014, the first commit to the GitHub book repository was pushed.

Around the same time, in a parallel universe, Keyvan co-founded Funddy, a crowdfunding platform for the masses built on top of the concepts and building blocks of Domain-Driven Design. Domain-Driven Design proved itself effective in the exploratory process and modeling of building an early-stage startup like Funddy. It also helped handle the complexity of the company, with its constantly changing environment and requirements. And after connecting with Carlos and Christian and discussing the book, Keyvan proudly signed on as the third writer.

Together, we’ve written the book we wanted to have when we started with Domain-Driven Design. It’s full of examples, production-ready code, shortcuts, and our recommendations based on our experiences of what worked and what didn’t for our respective teams. We arrived at Domain-Driven Design via its building blocks — Tactical Patterns — which is why this book is mainly about them. Reading it will help you learn them, write them, and implement them. You’ll also discover how to integrate Bounded Contexts using synchronous and asynchronous approaches, which will open your world to strategic design — though the latter is a road you’ll have to discover on your own.

This book is heavily inspired by Implementing Domain-Driven Design by Vaughn Vernon (aka the Red Book), and Domain-Driven Design: Tackling Complexity in the Heart of Software by Eric Evans (aka the Blue Book). You should buy both books. You should read them carefully. You should love them.

Who Should Read This Book

If you’re a PHP Developer, Architect, or Tech Lead, we highly recommend this book. It will help you become a better professional. It will give you a new overview of and approach to the applications you’re developing. If you’re a Junior profile, getting into Value Objects, Entities, Repositories, and Domain Events is important in order to model any Domain you’ll face in the future. For an average profile, understanding the benefits of Hexagonal Architecture and the boundaries between your framework and your Application is key for writing code that’s easier to maintain in the real world (framework migrations, testing, etc.). More advanced readers will have fun both exploring how to use Domain Events in order to integrate Applications and delving deeper into Aggregate design.

Although Domain-Driven Design is not about technology, you still need it to make HTTP requests to access your Domain. Throughout the book, we recommend using specific PHP frameworks and libraries, such as Symfony, Silex, and Doctrine. For some examples, we also use specific technologies, such as MySQL, RabbitMQ, Redis, and Elasticsearch. However, most important are the behind-the-scenes concepts — concepts that are applicable regardless of the technology used to implement them.

Additionally, the book is loaded with tons of details and examples, such as how to properly design and implement all the building blocks of Domain-Driven Design — including Value Objects, Entities, Services, Domain Events, Aggregates, Factories, Repositories, and Application Services — with PHP. It explains what the role of the main PHP libraries and frameworks used in Domain-Driven Design are. The book also teaches how to apply Hexagonal Architecture within your application, regardless of whether you use an open source framework or your own one. Finally, it shows how to integrate Bounded Contexts using REST frameworks and messaging mechanisms. If you’re interested in any of these subjects, this book is for you.

DDD and PHP Community

In 2016, Carlos and Christian went to the first official Domain-Driven Design conference, DDD Europe. They were really happy to see some PHP open source leaders, such as Marco Pivetta (Doctrine) and Sebastian Bergmann (PHPUnit), attending the conference.

Domain-Driven Design arrived in the PHP community two years prior to that conference. However, there’s still a lack of documentation and real code examples. Why? We think not many people have worked with this kind of approach in production yet — even people in other more established communities such as Java. Maybe this is because their project complexity is low, or maybe it’s because they don’t know how to do it. Whatever the reason, this book is written for the community. One of our goals is to teach you how you can write an application that solves your Domain issues without being coupled to specific frameworks or technologies.

Summary of Chapters

The book is arranged with each chapter exploring a separate tactical building block of Domain-Driven Design. It also includes an introduction to Domain-Driven Design, information on how to integrate different Bounded Contexts or applications, and an appendix.

Chapter 1: Getting Started with Domain-Driven Design

What is Domain-Driven Design about? What role does it play in complex systems? Is it worth learning about and exploring? What are the main concepts a developer needs to know when jumping into it?

Chapter 2: Architectural Styles

Bounded Contexts can be implemented in different ways and using different approaches. However, two styles are getting more popular, and they are Hexagonal Architecture and CQRS + ES. In this chapter, we’ll see these two main Architectural Styles, understand what their main strengths are, and discover when to use them.

Chapter 3: Value Objects

Value Objects are the basic pieces for rich modeling. We’ll learn what their properties are and what makes them so important. We’ll figure out how to persist them using Doctrine and custom ORMs. We’ll show how to properly validate and unit test them. And finally, we’ll see what a test case of testing immutability looks like.

Chapter 4: Entities

Entities are Domain-Driven Design building blocks that are uniquely identified and mutable. We’ll see how to create and validate them and how to properly map them using a custom ORM and Doctrine. We’ll also assess whether or not annotations are the best mapping approach for Entities and look at the different strategies for generating an Identity.

Chapter 5: Domain Services

In this chapter, you’ll learn about what a Domain Service is and when to use it. We’ll review what Anemic Domain Models and Rich Domain Models are. Lastly, we’ll deal with Infrastructure issues when writing Domain Services.

Chapter 6: Domain Events

Domain Events are a great Inversion of Control (IoC) mechanism. In Domain-Driven Design, they’re important for communicating different Bounded Contexts asynchronously, improving your Application performance using eventual consistency, and decoupling your Application from its Infrastructure.

Chapter 7: Modules

With so many tactical building blocks, it’s a bit difficult to know where to place them in code, especially if you’re dealing with a framework like Symfony. We’ll review how PHP namespaces can be used for implementing Modules. We’ll also discover different hierarchies of folders for organizing Domain Model code, Application Code, and Infrastructure Code.

Chapter 8: Aggregates

Aggregates are probably the most difficult part of tactical Domain-Driven Design. We’ll look at the key concepts when dealing with them and discover how to design them. We’ll also propose a practical scenario where two Aggregates become one when adding a business rule, and we’ll demonstrate how the rest of the objects must be refactored.

Chapter 9: Factories

Factory Methods and objects help us keep business invariants, which is why they’re so important in Domain-Driven Design. Here, we’ll also explore the relationship between Factories and Aggregates.

Chapter 10: Repositories

Repositories are key for retrieving and adding Entities and Aggregates to collections. We’ll review the different types of Repositories and learn how to implement them using Doctrine, custom ORMs, and Redis.

Chapter 11: Application

An Application is the thin layer that connects outside clients to your Domain. In this chapter, we’ll show you how to write your Application Services so that they’re easy to test and keep thin. We’ll also review how to prepare request objects, define dependencies, and return results.

Chapter 12: Integrating Bounded Contexts

We’ll explore the different tactical approaches to communicate Bounded Contexts and see real implementations. REST is our suggestion for synchronous communication, and messaging with RabbitMQ is our suggestion for asynchronous communication.

Appendix: Hexagonal Architecture with PHP

Here is where you’ll find the original article written by Carlos and published by php[architect] in June 2014.

Code and Examples

The authors have created an organization at GitHub called Domain-Driven Design in PHP, which is where all the code examples from this book, additional snippets, and some complete sample projects are available. For example, you can find Last Wishes, a simple Domain-Driven Design-style application showing different examples explained in this book. Additionally, you’ll find our CQRS Blog, along with Gamify, a Bounded Context that adds gamification capabilities to Last Wishes.

Finally, if you find any issue or fix or have a suggestion or comment while reading this book, you can create an issue in the DDD in PHP Book Issues repository. We fix them as they come in. If you’re interested, we also urge you to watch our projects and provide feedback.

Acknowledgements

First of all, we would like to thank all our friends and family. Without their support, writing this book would have been an even more difficult task. Thanks for accommodating our schedules and taking care of our children in order to free up time for us to focus on writing. You’re wonderful, and part of this book is also yours.

We are three Spaniards who wrote a book in English, so if you’d guess our English is far from perfect, you’d be correct. Luckily for us, Edd Mann has supported us with the language since the beginning. He’s not just a great collaborator but also a great friend, and we owe him a huge thanks. The final review was done by the professional copy editor Natalye Childress. She has done a great work rewriting our words to make them understandable. Thank you so much. Our book is easier and more enjoyable to read.

A group of PHP developers in Barcelona defends what we call el camino del rigor, or the path of rigor. It existed before the craftsmanship movement, and it means to struggle with everything stacked against us in order to build exceptional things in an exceptional way. Two particular developers and friends from that group are Albert Casademont and Ricard Clau, both of whom are extraordinary people committed to the community. Thank you so much for helping with the revision process. Your contributions have been incredibly valuable.

We would like to thank every developer who has worked with us in the companies where we’ve applied Domain-Driven Design. We know you’ve been struggling when learning and applying these concepts. Some of you weren’t so open-minded at the beginning, but after using the basic building blocks for a while, you became evangelists. Thanks for your faith.

Our book was for sale from the moment we put the first chapters on Leanpub. Early adopters who bought the book in its beginning stages gave us the much needed love and support to get this done. Thanks for the motivation to keep going.

Thanks also to Matthias Noback for his foreword and feedback on the book. The end result is better because of his contributions.

A special mention to Vaughn Vernon — not just because his work was an incredible source of information and inspiration for us, but also because he helped us find a good publisher, gave us valuable advice, and shared ideas with us. Thanks so much for your help.

Last but not least, we’d like to express our gratitude to all the people who have reported issues, made suggestions, and otherwise contributed to our GitHub repository. To all of you, thank you. You’ve helped us make this book better. More importantly, you’ve helped the community grow and helped other developers be better developers. As Robert C. Martin wrote in his book, Clean Code: A Handbook of Agile Software Craftsmanship, “You are reading this book for two reasons. First, you are a programmer. Second, you want to be a better programmer. Good. We need better programmers.” So thanks to Jordi Abad, Jonathan Wondrusch, César Rodríguez, Yannick Voyer, Victor Guardiola, Oriol González, Henry Snoek, Tom Jowitt, Sascha Schimke, Sven Herrmann, Daniel Abad, Luis Rovirosa, Luis Cordova, Raúl Ramos, Juan Maturana, Nil Portugués, Nikolai Zujev, Fernando Pradas, Raúl Araya, Neal Brooks, Hubert Béague, Aleksander Rekść, Sebastian Machuca, Nicolas Oelgart, Sebastiaan Stok, Vladimir Hraban, Vladas Dirzys, Max Gulturyan, Ivan Đurđevac, Marc Verney, Matthias Gutjahr, Dennis König, Jordi Puig, David Fernández, Vladimir Shadyan and Marc Aube.

About the Authors

Carlos Buenosvinos

Carlos is a PHP Extreme Programmer with more than 15 years of experience developing web applications and more than 10 years experience as a Tech Lead and CTO leading teams of between 20 and 100 people. He is a Certified ScrumMaster (CSM) and has coached and trained close to two dozen different companies in Agile practices, both as an employee and as a consultant. On the technical side, he is a Zend PHP Engineer, a Zend Framework Engineer, and MySQL certified. He is also a board member of the PHP Barcelona User Group. He has worked with e-commerce (Atrapalo and eBay), payment processing (Vendo), classifieds (Emagister), B2B recruiting tools (XING), and currently being General Director of SEAT:CODE. He is interested in JavaScript, DevOps, and Scala. He likes developing for mobile, Raspberry Pi, and games.

Christian Soronellas

Christian is a passionate Software Developer, Software Journeyman, and Crafter Apprentice. He’s an Extreme Programmer soul with more than 10 years of experience in web development. He’s also a Zend PHP 5.3 Certified Engineer, a Zend Framework Certified Engineer, and a SensioLabs Certified Symfony Developer. He has worked as a freelancer and consultant, as well as at Privalia, Emagister, Atrapalo, and Enalquiler as a Software Architect.

Keyvan Akbary

Keyvan is a polyglot Software Developer and Tech Leader who is in love with Software fundamentals, Crafter movement, Extreme Programming, SOLID principles, Clean Code, Design Patterns, and Testing. He’s also a sporadic Functional Programmer. He understands technology as a medium for providing value. He has worked on countless projects as a freelancer, on video streaming (Youzee), online marketplace (MyBuilder), in addition to founding a crowdfunding company (Funddy), and leading FinTech teams (TransferWise). Currently he is leading engineering in the ride-hailing space as Head of Engineering at Cabify.

Bibliography

Beck, Kent. Test-Driven Development: By Example. Addison-Wesley Professional, 2002.

Brandolini, Alberto. Introducing EventStorming. Leanpub, 2016.

Evans, Eric. Domain-Driven Design Reference: Definitions and Pattern Summaries. Dog Ear Publishing, 2014.

Evans, Eric. Domain-Driven Design: Tackling Complexity in the Heart of Software. Addison-Wesley Professional, 2003.

Fowler, Martin. Patterns of Enterprise Application Architecture. Addison-Wesley Professional, 2002.

Hohpe, Gregor, and Bobby Woolf. Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions. Addison-Wesley Professional, 2012.

Martin, Robert C. Agile Software Development, Principles, Patterns, and Practices. Pearson, 2002.

Martin, Robert C. Clean Code: A Handbook of Agile Software Craftsmanship. Prentice Hall, 2008.

Meszaros, Gerard. xUnit Test Patterns: Refactoring Test Code. Addison-Wesley Professional, 2007.

Newman, Sam. Building Microservices. O’Reilly Media, 2015.

Nilsson, Jimmy. Applying Domain-Driven Design and Patterns: With Examples in C# and .NET. Addison-Wesley Professional, 2006.

Sadalage, Pramod J., and Martin Fowler. NoSQL Distilled: A Brief Guide to the Emerging World of Polyglot Persistence. Addison-Wesley Professional, 2012.

Vernon, Vaughn. Domain-Driven Design Distilled. Addison-Wesley Professional, 2016.

Vernon, Vaughn. Implementing Domain-Driven Design. Addison-Wesley Professional, 2013.

Appendix: Hexagonal Architecture with PHP

The following article was posted in php|architect magazine in June 2014 by Carlos Buenosvinos.

Introduction

With the rise of Domain-Driven Design (DDD), architectures promoting domain centric designs are becoming more popular. This is the case with Hexagonal Architecture, also known as Ports and Adapters, that seems to have being rediscovered just now by PHP developers. Invented in 2005 by Alistair Cockburn, one of the Agile Manifesto authors, the Hexagonal Architecture allows an application to be equally driven by users, programs, automated tests or batch scripts, and to be developed and tested in isolation from its eventual run-time devices and databases. This results into agnostic infrastructure web applications that are easier to test, write and maintain. Let’s see how to apply it using real PHP examples.

Your company is building a brainstorming system called Idy. Users add and rate ideas so the most interesting ones can be implemented in a company. It is Monday morning, another sprint is starting and you are reviewing some user stories with your team and your Product Owner. “As a not logged in user, I want to rate an idea and the author should be notified by email”, that’s a really important one, isn’t it?

First Approach

As a good developer, you decide to divide and conquer the user story, so you’ll start with the first part, “I want to rate an idea”. After that, you will face “the author should be notified by email”. That sounds like a plan.

In terms of business rules, rating an idea is as easy as finding the idea by its identifier in the ideas repository, where all the ideas live, add the rating, recalculate the average and save the idea back. If the idea does not exist or the repository is not available we should throw an exception so we can show an error message, redirect the user or do whatever the business asks us for.

In order to execute this UseCase, we just need the idea identifier and the rating from the user. Two integers that would come from the user request.

Your company web application is dealing with a Zend Framework 1 legacy application. As most of companies, probably some parts of your app may be newer, more SOLID, and others may just be a big ball of mud. However, you know that it does not matter at all which framework you are using, it is all about writing clean code that makes maintenance a low cost task for your company.

You’re trying to apply some Agile principles you remember from your last conference, how it was, yeah, I remember “make it work, make it right, make it fast”. After some time working you get something like Listing 1.

 1 class IdeaController extends Zend_Controller_Action
 2 {
 3     public function rateAction()
 4     {
 5         // Getting parameters from the request
 6         $ideaId = $this->request->getParam('id');
 7         $rating = $this->request->getParam('rating');
 8 
 9         // Building database connection
10         $db = new Zend_Db_Adapter_Pdo_Mysql([
11             'host'     => 'localhost',
12             'username' => 'idy',
13             'password' => '',
14             'dbname'   => 'idy'
15         ]);
16 
17         // Finding the idea in the database
18         $sql = 'SELECT * FROM ideas WHERE idea_id = ?';
19         $row = $db->fetchRow($sql, $ideaId);
20         if (!$row) {
21             throw new Exception('Idea does not exist');
22         }
23 
24         // Building the idea from the database
25         $idea = new Idea();
26         $idea->setId($row['id']);
27         $idea->setTitle($row['title']);
28         $idea->setDescription($row['description']);
29         $idea->setRating($row['rating']);
30         $idea->setVotes($row['votes']);
31         $idea->setAuthor($row['email']);
32 
33         // Add user rating
34         $idea->addRating($rating);
35 
36         // Update the idea and save it to the database
37         $data = [
38             'votes' => $idea->getVotes(),
39             'rating' => $idea->getRating()
40         ];
41         $where['idea_id = ?'] = $ideaId;
42         $db->update('ideas', $data, $where);
43 
44         // Redirect to view idea page
45         $this->redirect('/idea/'.$ideaId);
46     }
47 }

I know what readers are thinking: “Who is going to access data directly from the controller? This is a 90’s example!”, ok, ok, you’re right. If you are already using a framework, it is likely that you are also using an ORM. Maybe done by yourself or any of the existing ones such as Doctrine, Eloquent, ZendDB, etc. If this is the case, you are one step further from those who have some Database connection object but don’t count your chickens before they’re hatched.

For newbies, Listing 1 code just works. However, if you take a closer look at the Controller, you’ll see more than business rules, you’ll also see how your web framework routes a request into your business rules, references to the database or how to connect to it. So close, you see references to your infrastructure.

Infrastructure is the detail that makes your business rules work. Obviously, we need some way to get to them (API, web, console apps, etc.) and effectively we need some physical place to store our ideas (memory, database, NoSQL, etc.). However, we should be able to exchange any of these pieces with another that behaves in the same way but with different implementations. What about starting with the Database access?

All those Zend_DB_Adapter connections (or straight MySQL commands if that’s your case) are asking to be promoted to some sort of object that encapsulates fetching and persisting Idea objects. They are begging for being a Repository.

Repositories and the Persistence Edge

Whether there is a change in the business rules or in the infrastructure, we must edit the same piece of code. Believe me, in CS, you don’t want many people touching the same piece of code for different reasons. Try to make your functions do one and just one thing so it is less probable having people messing around with the same piece of code. You can learn more about this by having a look at the Single Responsibility Principle (SRP). For more information about this principle: http://www.objectmentor.com/resources/articles/srp.pdf

Listing 1 is clearly this case. If we want to move to Redis or add the author notification feature, you’ll have to update the rateAction method. Chances to affect aspects of the rateAction not related with the one updating are high. Listing 1 code is fragile. If it is common in your team to hear “If it works, don’t touch it”, SRP is missing.

So, we must decouple our code and encapsulate the responsibility for dealing with fetching and persisting ideas into another object. The best way, as explained before, is using a Repository. Challenged accepted! Let’s see the results in Listing 2.

 1 class IdeaController extends Zend_Controller_Action
 2 {
 3     public function rateAction()
 4     {
 5         $ideaId = $this->request->getParam('id');
 6         $rating = $this->request->getParam('rating');
 7 
 8         $ideaRepository = new IdeaRepository();
 9         $idea = $ideaRepository->find($ideaId);
10         if (!$idea) {
11             throw new Exception('Idea does not exist');
12         }
13 
14         $idea->addRating($rating);
15         $ideaRepository->update($idea);
16 
17         $this->redirect('/idea/'.$ideaId);
18     }
19 }
20 
21 class IdeaRepository
22 {
23     private $client;
24 
25     public function __construct()
26     {
27         $this->client = new Zend_Db_Adapter_Pdo_Mysql([
28             'host'     => 'localhost',
29             'username' => 'idy',
30             'password' => '',
31             'dbname'   => 'idy'
32         ]);
33     }
34 
35     public function find($id)
36     {
37         $sql = 'SELECT * FROM ideas WHERE idea_id = ?';
38         $row = $this->client->fetchRow($sql, $id);
39         if (!$row) {
40             return null;
41         }
42 
43         $idea = new Idea();
44         $idea->setId($row['id']);
45         $idea->setTitle($row['title']);
46         $idea->setDescription($row['description']);
47         $idea->setRating($row['rating']);
48         $idea->setVotes($row['votes']);
49         $idea->setAuthor($row['email']);
50 
51         return $idea;
52     }
53 
54     public function update(Idea $idea)
55     {
56         $data = [
57             'title' => $idea->getTitle(),
58             'description' => $idea->getDescription(),
59             'rating' => $idea->getRating(),
60             'votes' => $idea->getVotes(),
61             'email' => $idea->getAuthor(),
62         ];
63 
64         $where = ['idea_id = ?' => $idea->getId()];
65         $this->client->update('ideas', $data, $where);
66     }
67 }

The result is nicer. The rateAction of the IdeaController is more understandable. When read, it talks about business rules. IdeaRepository is a business concept. When talking with business guys, they understand what an IdeaRepository is: A place where I put Ideas and get them.

A Repository “mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects.” as found in Martin Fowler’s pattern catalog.

If you are already using an ORM such as Doctrine, your current repositories extend from an EntityRepository. If you need to get one of those repositories, you ask Doctrine EntityManager to do the job. The resulting code would be almost the same, with an extra access to the EntityManager in the controller action to get the IdeaRepository.

At this point, we can see in the landscape one of the edges of our hexagon, the persistence edge. However, this side is not well drawn, there is still some relationship between what an IdeaRepository is and how it is implemented.

In order to make an effective separation between our application boundary and the infrastructure boundary we need an additional step. We need to explicitly decouple behavior from implementation using some sort of interface.

Decoupling Business and Persistence

Have you ever experienced the situation when you start talking to your Product Owner, Business Analyst or Project Manager about your issues with the Database? Can you remember their faces when explaining how to persist and fetch an object? They had no idea what you were talking about.

The truth is that they don’t care, but that’s ok. If you decide to store the ideas in a MySQL server, Redis or SQLite it is your problem, not theirs. Remember, from a business standpoint, your infrastructure is a detail. Business rules are not going to change whether you use Symfony or Zend Framework, MySQL or PostgreSQL, REST or SOAP, etc.

That’s why it is important to decouple our IdeaRepository from its implementation. The easiest way is to use a proper interface. How can we achieve that? Let’s take a look at Listing 3.

 1 class IdeaController extends Zend_Controller_Action
 2 {
 3     public function rateAction()
 4     {
 5         $ideaId = $this->request->getParam('id');
 6         $rating = $this->request->getParam('rating');
 7 
 8         $ideaRepository = new MySQLIdeaRepository();
 9         $idea = $ideaRepository->find($ideaId);
10         if (!$idea) {
11             throw new Exception('Idea does not exist');
12         }
13 
14         $idea->addRating($rating);
15         $ideaRepository->update($idea);
16 
17         $this->redirect('/idea/'.$ideaId);
18     }
19 }
20 
21 interface IdeaRepository
22 {
23     /**
24      * @param int $id
25      * @return null|Idea
26      */
27     public function find($id);
28 
29     /**
30      * @param Idea $idea
31      */
32     public function update(Idea $idea);
33 }
34 
35 class MySQLIdeaRepository implements IdeaRepository
36 {
37     // ...
38 }

Easy, isn’t it? We have extracted the IdeaRepository behavior into an interface, renamed the IdeaRepository into MySQLIdeaRepository and updated the rateAction to use our MySQLIdeaRepository. But what’s the benefit?

We can now exchange the repository used in the controller with any implementing the same interface. So, let’s try a different implementation.

Migrating our Persistence to Redis

During the sprint and after talking to some mates, you realize that using a NoSQL strategy could improve the performance of your feature. Redis is one of your best friends. Go for it and show me your Listing 4.

 1 class IdeaController extends Zend_Controller_Action
 2 {
 3     public function rateAction()
 4     {
 5         $ideaId = $this->request->getParam('id');
 6         $rating = $this->request->getParam('rating');
 7 
 8         $ideaRepository = new RedisIdeaRepository();
 9         $idea = $ideaRepository->find($ideaId);
10         if (!$idea) {
11             throw new Exception('Idea does not exist');
12         }
13 
14         $idea->addRating($rating);
15         $ideaRepository->update($idea);
16 
17         $this->redirect('/idea/'.$ideaId);
18     }
19 }
20 
21 interface IdeaRepository
22 {
23     // ...
24 }
25 
26 class RedisIdeaRepository implements IdeaRepository
27 {
28     private $client;
29 
30     public function __construct()
31     {
32         $this->client = new \Predis\Client();
33     }
34 
35     public function find($id)
36     {
37         $idea = $this->client->get($this->getKey($id));
38         if (!$idea) {
39             return null;
40         }
41 
42         return unserialize($idea);
43     }
44 
45     public function update(Idea $idea)
46     {
47         $this->client->set(
48             $this->getKey($idea->getId()),
49             serialize($idea)
50         );
51     }
52 
53     private function getKey($id)
54     {
55         return 'idea:' . $id;
56     }
57 }

Easy again. You’ve created a RedisIdeaRepository that implements IdeaRepository interface and we have decided to use Predis as a connection manager. Code looks smaller, easier and faster. But what about the controller? It remains the same, we have just changed which repository to use, but it was just one line of code.

As an exercise for the reader, try to create the IdeaRepository for SQLite, a file or an in-memory implementation using arrays. Extra points if you think about how ORM Repositories fit with Domain Repositories and how ORM @annotations affect this architecture.

Decouple Business and Web Framework

We have already seen how easy it can be to changing from one persistence strategy to another. However, the persistence is not the only edge from our Hexagon. What about how the user interacts with the application?

Your CTO has set up in the roadmap that your team is moving to Symfony2, so when developing new features in you current ZF1 application, we would like to make the incoming migration easier. That’s tricky, show me your Listing 5.

 1 class IdeaController extends Zend_Controller_Action
 2 {
 3     public function rateAction()
 4     {
 5         $ideaId = $this->request->getParam('id');
 6         $rating = $this->request->getParam('rating');
 7 
 8         $ideaRepository = new RedisIdeaRepository();
 9         $useCase = new RateIdeaUseCase($ideaRepository);
10         $response = $useCase->execute($ideaId, $rating);
11 
12         $this->redirect('/idea/'.$ideaId);
13     }
14 }
15 
16 interface IdeaRepository
17 {
18     // ...
19 }
20 
21 class RateIdeaUseCase
22 {
23     private $ideaRepository;
24 
25     public function __construct(IdeaRepository $ideaRepository)
26     {
27         $this->ideaRepository = $ideaRepository;
28     }
29 
30     public function execute($ideaId, $rating)
31     {
32         try {
33             $idea = $this->ideaRepository->find($ideaId);
34         } catch(Exception $e) {
35             throw new RepositoryNotAvailableException();
36         }
37 
38         if (!$idea) {
39             throw new IdeaDoesNotExistException();
40         }
41 
42         try {
43             $idea->addRating($rating);
44             $this->ideaRepository->update($idea);
45         } catch(Exception $e) {
46             throw new RepositoryNotAvailableException();
47         }
48 
49         return $idea;
50     }
51 }

Let’s review the changes. Our controller is not having any business rules at all. We have pushed all the logic inside a new object called RateIdeaUseCase that encapsulates it. This object is also known as Controller, Interactor or Application Service.

The magic is done by the execute method. All the dependencies such as the RedisIdeaRepository are passed as an argument to the constructor. All the references to an IdeaRepository inside our UseCase are pointing to the interface instead of any concrete implementation.

That’s really cool. If you take a look inside RateIdeaUseCase, there is nothing talking about MySQL or Zend Framework. No references, no instances, no annotations, nothing. It is like your infrastructure does not mind. It just talks about business logic.

Additionally, we have also tuned the Exceptions we throw. Business processes also have exceptions. NotAvailableRepository and IdeaDoesNotExist are two of them. Based on the one being thrown we can react in different ways in the framework boundary.

Sometimes, the number of parameters that a UseCase receives can be too many. In order to organize them, it is quite common to build a UseCase request using a Data Transfer Object (DTO) to pass them together. Let’s see how you could solve this in Listing 6.

 1 class IdeaController extends Zend_Controller_Action
 2 {
 3     public function rateAction()
 4     {
 5         $ideaId = $this->request->getParam('id');
 6         $rating = $this->request->getParam('rating');
 7 
 8         $ideaRepository = new RedisIdeaRepository();
 9         $useCase = new RateIdeaUseCase($ideaRepository);
10         $response = $useCase->execute(
11             new RateIdeaRequest($ideaId, $rating)
12         );
13 
14         $this->redirect('/idea/'.$response->idea->getId());
15     }
16 }
17 
18 class RateIdeaRequest
19 {
20     public $ideaId;
21     public $rating;
22 
23     public function __construct($ideaId, $rating)
24     {
25         $this->ideaId = $ideaId;
26         $this->rating = $rating;
27     }
28 }
29 
30 class RateIdeaResponse
31 {
32     public $idea;
33 
34     public function __construct(Idea $idea)
35     {
36         $this->idea = $idea;
37     }
38 }
39 
40 class RateIdeaUseCase
41 {
42     // ...
43 
44     public function execute($request)
45     {
46         $ideaId = $request->ideaId;
47         $rating = $request->rating;
48 
49         // ...
50 
51         return new RateIdeaResponse($idea);
52     }
53 }

The main changes here are introducing two new objects, a Request and a Response. They are not mandatory, maybe a UseCase has no request or response. Another important detail is how you build this request. In this case, we are building it getting the parameters from ZF request object.

Ok, but wait, what’s the real benefit? it is easier to change from one framework to other, or execute our UseCase from another delivery mechanism. Let’s see this point.

Rating an idea using the API

During the day, your Product Owner comes to you and says: “by the way, a user should be able to rate an idea using our mobile app. I think we will need to update the API, could you do it for this sprint?”. Here’s the PO again. “No problem!”. Business is impressed with your commitment.

As Robert C. Martin says: “The Web is a delivery mechanism […] Your system architecture should be as ignorant as possible about how it is to be delivered. You should be able to deliver it as a console app, a web app, or even a web service app, without undue complication or any change to the fundamental architecture”.

Your current API is built using Silex, the PHP micro-framework based on the Symfony2 Components. Let’s go for it in Listing 7.

 1 require_once __DIR__.'/../vendor/autoload.php';
 2 
 3 $app = new \Silex\Application();
 4 
 5 // ... more routes
 6 
 7 $app->get(
 8     '/api/rate/idea/{ideaId}/rating/{rating}',
 9     function ($ideaId, $rating) use ($app) {
10         $ideaRepository = new RedisIdeaRepository();
11         $useCase = new RateIdeaUseCase($ideaRepository);
12         $response = $useCase->execute(
13             new RateIdeaRequest($ideaId, $rating)
14         );
15 
16         return $app->json($response->idea);
17     }
18 );
19 
20 $app->run();

Is there anything familiar to you? Can you identify some code that you have seen before? I’ll give you a clue.

1 $ideaRepository = new RedisIdeaRepository();
2 $useCase = new RateIdeaUseCase($ideaRepository);
3 $response = $useCase->execute(
4     new RateIdeaRequest($ideaId, $rating)
5 );

“Man! I remember those 3 lines of code. They look exactly the same as the web application”. That’s right, because the UseCase encapsulates the business rules you need to prepare the request, get the response and act accordingly.

We are providing our users with another way for rating an idea; another delivery mechanism.

The main difference is where we created the RateIdeaRequest from. In the first example, it was from a ZF request and now it is from a Silex request using the parameters matched in the route.

Console app rating

Sometimes, a UseCase is going to be executed from a Cron job or the command line. As examples, batch processing or some testing command lines to accelerate the development.

While testing this feature using the web or the API, you realize that it would be nice to have a command line to do it, so you don’t have to go through the browser.

If you are using shell scripts files, I suggest you to check the Symfony Console component. What would the code look like?

 1 namespace Idy\Console\Command;
 2 
 3 use Symfony\Component\Console\Command\Command;
 4 use Symfony\Component\Console\Input\InputArgument;
 5 use Symfony\Component\Console\Input\InputInterface;
 6 use Symfony\Component\Console\Output\OutputInterface;
 7 
 8 class VoteIdeaCommand extends Command
 9 {
10     protected function configure()
11     {
12         $this
13             ->setName('idea:rate')
14             ->setDescription('Rate an idea')
15             ->addArgument('id', InputArgument::REQUIRED)
16             ->addArgument('rating', InputArgument::REQUIRED)
17         ;
18     }
19 
20     protected function execute(
21         InputInterface $input,
22         OutputInterface $output
23     ) {
24         $ideaId = $input->getArgument('id');
25         $rating = $input->getArgument('rating');
26 
27         $ideaRepository = new RedisIdeaRepository();
28         $useCase = new RateIdeaUseCase($ideaRepository);
29         $response = $useCase->execute(
30             new RateIdeaRequest($ideaId, $rating)
31         );
32 
33         $output->writeln('Done!');
34     }
35 }

Again those 3 lines of code. As before, the UseCase and its business logic remain untouched, we are just providing a new delivery mechanism. Congratulations, you’ve discovered the user side hexagon edge.

There is still a lot to do. As you may have heard, a real craftsman does TDD. We have already started our story so we must be ok with just testing after.

Testing Rating an Idea UseCase

Michael Feathers introduced a definition of legacy code as code without tests. You don’t want your code to be legacy just born, do you?

In order to unit test this UseCase object, you decide to start with the easiest part, what happens if the repository is not available? How can we generate such behavior? Do we stop our Redis server while running the unit tests? No. We need to have an object that has such behavior. Let’s use a mock object in Listing 9.

 1 class RateIdeaUseCaseTest extends \PHPUnit_Framework_TestCase
 2 {
 3     /**
 4      * @test
 5      */
 6     public function whenRepositoryNotAvailableAnExceptionShouldBeThrown()
 7     {
 8         $this->setExpectedException('NotAvailableRepositoryException');
 9         $ideaRepository = new NotAvailableRepository();
10         $useCase = new RateIdeaUseCase($ideaRepository);
11         $useCase->execute(
12             new RateIdeaRequest(1, 5)
13         );
14     }
15 }
16 
17 class NotAvailableRepository implements IdeaRepository
18 {
19     public function find($id)
20     {
21         throw new NotAvailableException();
22     }
23 
24     public function update(Idea $idea)
25     {
26         throw new NotAvailableException();
27     }
28 }

Nice. NotAvailableRepository has the behavior that we need and we can use it with RateIdeaUseCase because it implements IdeaRepository interface.

Next case to test is what happens if the idea is not in the repository. Listing 10 shows the code.

 1 class RateIdeaUseCaseTest extends \PHPUnit_Framework_TestCase
 2 {
 3     // ...
 4 
 5     /**
 6      * @test
 7      */
 8     public function whenIdeaDoesNotExistAnExceptionShouldBeThrown()
 9     {
10         $this->setExpectedException('IdeaDoesNotExistException');
11         $ideaRepository = new EmptyIdeaRepository();
12         $useCase = new RateIdeaUseCase($ideaRepository);
13         $useCase->execute(
14             new RateIdeaRequest(1, 5)
15         );
16     }
17 }
18 
19 class EmptyIdeaRepository implements IdeaRepository
20 {
21     public function find($id)
22     {
23         return null;
24     }
25 
26     public function update(Idea $idea)
27     {
28 
29     }
30 }

Here, we use the same strategy but with an EmptyIdeaRepository. It also implements the same interface but the implementation always returns null regardless which identifier the find method receives.

Why are we testing these cases?, remember Kent Beck’s words: “Test everything that could possibly break”.

Let’s carry on with the rest of the feature. We need to check a special case that is related with having a read available repository where we cannot write to. Solution can be found in Listing 11.

 1 class RateIdeaUseCaseTest extends \PHPUnit_Framework_TestCase
 2 {
 3     // ...
 4 
 5     /**
 6      * @test
 7      */
 8     public function whenUpdatingInReadOnlyAnIdeaAnExceptionShouldBeThrown()
 9     {
10         $this->setExpectedException('NotAvailableRepositoryException');
11         $ideaRepository = new WriteNotAvailableRepository();
12         $useCase = new RateIdeaUseCase($ideaRepository);
13         $response = $useCase->execute(
14             new RateIdeaRequest(1, 5)
15         );
16     }
17 }
18 
19 class WriteNotAvailableRepository implements IdeaRepository
20 {
21     public function find($id)
22     {
23         $idea = new Idea();
24         $idea->setId(1);
25         $idea->setTitle('Subscribe to php[architect]');
26         $idea->setDescription('Just buy it!');
27         $idea->setRating(5);
28         $idea->setVotes(10);
29         $idea->setAuthor('hi@carlos.io');
30 
31         return $idea;
32     }
33 
34     public function update(Idea $idea)
35     {
36         throw new NotAvailableException();
37     }
38 }

Ok, now the key part of the feature is still remaining. We have different ways of testing this, we can write our own mock or use a mocking framework such as Mockery or Prophecy. Let’s choose the first one. Another interesting exercise would be to write this example and the previous ones using one of these frameworks.

 1 class RateIdeaUseCaseTest extends \PHPUnit_Framework_TestCase
 2 {
 3     // ...
 4 
 5     /**
 6      * @test
 7      */
 8     public function whenRatingAnIdeaNewRatingShouldBeAddedAndIdeaUpdated()
 9     {
10         $ideaRepository = new OneIdeaRepository();
11         $useCase = new RateIdeaUseCase($ideaRepository);
12         $response = $useCase->execute(
13             new RateIdeaRequest(1, 5)
14         );
15 
16         $this->assertSame(5, $response->idea->getRating());
17         $this->assertTrue($ideaRepository->updateCalled);
18     }
19 }
20 
21 class OneIdeaRepository implements IdeaRepository
22 {
23     public $updateCalled = false;
24 
25     public function find($id)
26     {
27         $idea = new Idea();
28         $idea->setId(1);
29         $idea->setTitle('Subscribe to php[architect]');
30         $idea->setDescription('Just buy it!');
31         $idea->setRating(5);
32         $idea->setVotes(10);
33         $idea->setAuthor('hi@carlos.io');
34 
35         return $idea;
36     }
37 
38     public function update(Idea $idea)
39     {
40         $this->updateCalled = true;
41     }
42 }

Bam! 100% Coverage for the UseCase. Maybe, next time we can do it using TDD so the test will come first. However, testing this feature was really easy because of the way decoupling is promoted in this architecture.

Maybe you are wondering about this:

1 $this->updateCalled = true;

We need a way to guarantee that the update method has been called during the UseCase execution. This does the trick. This test double object is called a spy, mocks cousin.

When to use mocks? As a general rule, use mocks when crossing boundaries. In this case, we need mocks because we are crossing from the domain to the persistence boundary.

What about testing the infrastructure?

Testing Infrastructure

If you want to achieve 100% coverage for your whole application you will also have to test your infrastructure. Before doing that, you need to know that those unit tests will be more coupled to your implementation than the business ones. That means that the probability to be broken with implementation details changes is higher. So it is a trade-off you will have to consider.

So, if you want to continue, we need to do some modifications. We need to decouple even more. Let’s see the code in Listing 13.

 1 class IdeaController extends Zend_Controller_Action
 2 {
 3     public function rateAction()
 4     {
 5         $ideaId = $this->request->getParam('id');
 6         $rating = $this->request->getParam('rating');
 7 
 8         $useCase = new RateIdeaUseCase(
 9             new RedisIdeaRepository(
10                 new \Predis\Client()
11             )
12         );
13 
14         $response = $useCase->execute(
15             new RateIdeaRequest($ideaId, $rating)
16         );
17 
18         $this->redirect('/idea/'.$response->idea->getId());
19     }
20 }
21 
22 class RedisIdeaRepository implements IdeaRepository
23 {
24     private $client;
25 
26     public function __construct($client)
27     {
28         $this->client = $client;
29     }
30 
31     // ...
32 
33     public function find($id)
34     {
35         $idea = $this->client->get($this->getKey($id));
36         if (!$idea) {
37             return null;
38         }
39 
40         return $idea;
41     }
42 }

If we want to 100% unit test RedisIdeaRepository we need to be able to pass the Predis\Client as a parameter to the repository without specifying TypeHinting so we can pass a mock to force the code flow necessary to cover all the cases.

This forces us to update the Controller to build the Redis connection, pass it to the repository and pass the result to the UseCase.

Now, it is all about creating mocks, test cases and having fun doing asserts.

Arggg, So Many Dependencies!

Is it normal that I have to create so many dependencies by hand? No. It is common to use a Dependency Injection component or a Service Container with such capabilities. Again, Symfony comes to the rescue, however, you can also check PHP-DI 4 http://php-di.org/.

Let’s see the resulting code in Listing 14 after applying Symfony Service Container component to our application.

 1 class IdeaController extends ContainerAwareController
 2 {
 3     public function rateAction()
 4     {
 5         $ideaId = $this->request->getParam('id');
 6         $rating = $this->request->getParam('rating');
 7 
 8         $useCase = $this->get('rate_idea_use_case');
 9         $response = $useCase->execute(
10             new RateIdeaRequest($ideaId, $rating)
11         );
12 
13         $this->redirect('/idea/'.$response->idea->getId());
14     }
15 }

The controller has been modified to have access to the container, that’s why it is inheriting from a new base controller ContainerAwareController that has a get method to retrieve each of the services contained.

 1 <?xml version="1.0" ?>
 2 <container xmlns="http://symfony.com/schema/dic/services"
 3            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4            xsi:schemaLocation="http://symfony.com/schema/dic/services
 5            http://symfony.com/schema/dic/services/services-1.0.xsd">
 6     <services>
 7         <service
 8             id="rate_idea_use_case"
 9             class="RateIdeaUseCase">
10             <argument type="service" id="idea_repository" />
11         </service>
12 
13         <service
14             id="idea_repository"
15             class="RedisIdeaRepository">
16             <argument type="service">
17                 <service class="Predis\Client" />
18             </argument>
19         </service>
20     </services>
21 </container>

In Listing 15, you can also find the XML file used to configure the Service Container. It is really easy to understand but if you need more information, take a look to the Symfony Service Container Component site in http://symfony.com/doc/current/book/service_container.html

Domain Services and Notification Hexagon Edge

Are we forgetting something? “the author should be notified by email”, yeah! That’s true. Let’s see in Listing 16 how we have updated the UseCase for doing the job.

 1 class RateIdeaUseCase
 2 {
 3     private $ideaRepository;
 4     private $authorNotifier;
 5 
 6     public function __construct(
 7         IdeaRepository $ideaRepository,
 8         AuthorNotifier $authorNotifier
 9     )
10     {
11         $this->ideaRepository = $ideaRepository;
12         $this->authorNotifier = $authorNotifier;
13     }
14 
15     public function execute(RateIdeaRequest $request)
16     {
17         $ideaId = $request->ideaId;
18         $rating = $request->rating;
19 
20         try {
21             $idea = $this->ideaRepository->find($ideaId);
22         } catch(Exception $e) {
23             throw new RepositoryNotAvailableException();
24         }
25 
26         if (!$idea) {
27             throw new IdeaDoesNotExistException();
28         }
29 
30         try {
31             $idea->addRating($rating);
32             $this->ideaRepository->update($idea);
33         } catch(Exception $e) {
34             throw new RepositoryNotAvailableException();
35         }
36 
37         try {
38             $this->authorNotifier->notify(
39                 $idea->getAuthor()
40             );
41         } catch(Exception $e) {
42             throw new NotificationNotSentException();
43         }
44 
45         return $idea;
46     }
47 }

As you realize, we have added a new parameter for passing AuthorNotifier Service that will send the email to the author. This is the port in the “Ports and Adapters” naming. We have also updated the business rules in the execute method.

Repositories are not the only objects that may access your infrastructure and should be decoupled using interfaces or abstract classes. Domain Services can too. When there is a behavior not clearly owned by just one Entity in your domain, you should create a Domain Service. A typical pattern is to write an abstract Domain Service that has some concrete implementation and some other abstract methods that the adapter will implement.

As an exercise, define the implementation details for the AuthorNotifier abstract service. Options are SwiftMailer or just plain mail calls. It is up to you.

Let’s Recap

In order to have a clean architecture that helps you create easy to write and test applications, we can use Hexagonal Architecture. To achieve that, we encapsulate user story business rules inside a UseCase or Interactor object. We build the UseCase request from our framework request, instantiate the UseCase and all its dependencies and then execute it. We get the response and act accordingly based on it. If our framework has a Dependency Injection component you can use it to simplify the code.

The same UseCase objects can be used from different delivery mechanisms in order to allow users access the features from different clients (web, API, console, etc.)

For testing, play with mocks that behave like all the interfaces defined so special cases or error flows can also be covered. Enjoy the good job done.

Hexagonal Architecture

In almost all the blogs and books you will find drawings about concentric circles representing different areas of software. As Robert C. Martin explains in his “Clean Architecture” post, the outer circle is where your infrastructure resides. The inner circle is where your Entities live. The overriding rule that makes this architecture work is The Dependency Rule. This rule says that source code dependencies can only point inwards. Nothing in an inner circle can know anything at all about something in an outer circle.

Key Points

Use this approach if 100% unit test code coverage is important to your application. Also, if you want to be able to switch your storage strategy, web framework or any other type of third-party code. The architecture is especially useful for long-lasting applications that need to keep up with changing requirements.

What’s Next?

If you are interested in learning more about Hexagonal Architecture and other near concepts you should review the related URLs provided at the beginning of the article, take a look at CQRS and Event Sourcing. Also, don’t forget to subscribe to google groups and RSS about DDD such as http://dddinphp.org and follow on Twitter people like @VaughnVernon, and @ericevans0.