Table of Contents
- Foreword by Matthias Noback
- About the Authors
Appendix: Hexagonal Architecture with PHP
- First Approach
- Repositories and the Persistence Edge
- Decoupling Business and Persistence
- Migrating our Persistence to Redis
- Decouple Business and Web Framework
- Rating an idea using the API
- Console app rating
- Testing Rating an Idea UseCase
- Testing Infrastructure
- Arggg, So Many Dependencies!
- Domain Services and Notification Hexagon Edge
- Let’s Recap
- Hexagonal Architecture
- Key Points
- What’s Next?
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!
Author of A Year with Symfony
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.
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.
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.
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.
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?
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
Here is where you’ll find the original article written by Carlos and published by php[architect] in June 2014.
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.
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.
- Twitter: @buenosvinos
- Web: https://carlosbuenosvinos.com
- GitHub: https://github.com/carlosbuenosvinos
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 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.
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.
The following article was posted in php|architect magazine in June 2014 by Carlos Buenosvinos.
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?
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.
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?
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.
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.
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
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.
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.
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.
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.
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.
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.
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.
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.
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.
Is there anything familiar to you? Can you identify some code that you have seen before? I’ll give you a clue.
“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.
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?
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.
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.
NotAvailableRepository has the behavior that we need and we can use it with
RateIdeaUseCase because it implements
Next case to test is what happens if the idea is not in the repository. Listing 10 shows the code.
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.
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.
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:
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?
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.
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.
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.
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.
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
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.
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
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
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.
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.
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.
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.