My new book Recipes for Decoupling is 100% complete and available now!
And now some other news related to this book.
A little background information
My new book is based on two things: 20 years of experience with (mostly framework) coupling issues in legacy code, and the hypothesis that PHPStan, the automated static analysis tool for PHP, can help us keep our code decoupled. Decoupling often means we want to use a dependency, but don't want to couple our code too tightly to it. The process of decoupling often involves some kind of rule, like "don't use Container::get()", but we don't want to focus on this rule all the time, or explain it to new members of the team. Instead, we want a tool that shows you an error when you don't follow the rule. Such an error will break the build of the project and prevent you from ever coupling to a dependency in this specific way again.
The Architecture & Decoupling bundle
Although the book counts 300 pages, it doesn't cover too much theory, and shows a lot of practical code samples instead. You'll learn useful techniques to decouple your code in meaningful ways from frameworks and libraries. Many of these techniques, or recipes, would eventually lead to the discovery of design patterns. In fact, if you already know patterns like Aggregate, or Application Service, you'll quickly recognize them in this book. But this book focuses not on the patterns, but on the question: what's a meaningful way to decouple from the dependencies of my project?
Because I still think that it will be interesting to learn about both - using refactoring techniques and static analysis rules to stay decoupled, as well as using design patterns to establish a good architecture for your web applications - I also set up a bundle, providing a nice discount if you buy both "Advanced Web Application Architecture" and "Recipes for Decoupling": the Architecture & Decoupling Bundle.
PHPStan custom rules repository
In "Recipes for Decoupling" we use PHPStan to establish strict rules regarding the use of third-party code or to prevent certain structures in code. We develop these rules step by step and with steadily increasing levels of complexity, but it may be useful to take a look at the end result. For this purpose I've set up a public GitHub repository with the final version of each rule and the corresponding tests:
Interview with Leanpub
If you want to hear more about Leanpub and my experience with book writing on their platform, check out the Frontmatter interview I had with Len Epp recently.
The book
The most important bit: the book. Recipes for Decoupling is 100% done now and available via Leanpub. I will later distribute a paper version via Lulu, but this will take a few more weeks at least.
Both have pitfalls that seem immediately apparent, so I would like to hear if you have any suggestions for this sticky position?
Hi Matt, Thanks for you comment! In a legacy situation where we couldn't even add another column for the UUID, I have applied the following solution:
Call to repository interface
nextId()
, which returns a value object that wraps an integer. The infrastructure implementation just did a MAX(id) + 1, because the database had to keep using an auto-incrementing ID.Pass the ID value object to the entity so it would use it, and it would end up in the id column.
This worked fine, because there were almost no concurrent updates, so there would be no clashes between different processes trying to insert a record with that same ID. Since we used the Aggregate pattern and we would wrap the changes in a database transaction as well, we would never have inconsistent data. The worst that could happen was that the transaction was rolled back. The user would then just re-do the same action.
If there had been a concurrency issue, we could have improved this process:
We could introduce an actual sequence, that would actually reserve the generated ID for the requesting party.
We could retry the same command, re-doing the changes but with a newly generated ID.
I hope this is helpful in some way!
Back to this 😅 how do you handle getting then next ID for child entities? If I have
PurchaseOrder::addLine()
I am stuck either: a) creating theLine
outside the aggregate; the purchase order service already has the repository so can probably handle this withPurchaseOrderRepository::nextLineId()
. Or, b) fetching the next ID for the Line from within the aggregate, which means the aggregate has its own repository as a dependency; seems unwise.Thank you, very helpful. I had imagined updating the AUTO_INCREMENT value manually on the table (to deal with concurrency, which is definitely a consideration) but I actually like the “just increment and retry” solution a lot!
MAX(id)+1
should cover 99% of cases (and from the read replica) and the collision becomes just a single additional command. I appreciate the response!Thanks, Matt!
Good point about the breaking changes. I don't think many examples will break. I only thought of one example with Twig that uses
ArrayItem
, so that probably needs an update. Anyway, I'll make sure to publish an updated version of the book when the new major version of PHPStan lands.