Decouple your framework or library from your business logic for future upgrades or replacements through usage of interfaces. By separating your business logic completely from the tool used to glue all things together, you can replace your framework or upgrade to a newer version without much problems.
Many developers work on a legacy applications and are constantly battling with adding new features and reducing technical depth. Most of the time, business gets priority and a good application turns into a cluster of code ontop of a cluster of code. This is still ok until these developers need to upgrade their frameworks or libraries, maybe even switch from Subversion to GIT where a ton of new challenges pile up.
When using a framework or a (Composer) library, developers tend to drink the “cool-aid” of the tool within their business logic. Nothing wrong with that until there’s a new major version released and support for the version you use is ending. When we say “major version” we are looking at the first version number like Symfony 3.x.x, Zend Framework 2.x.x, etc… . Most of the time this indicates that there will be backwards compatibility breaks and even new ways of glueing the components together.
Modern frameworks implement a design pattern called “Dependency Injection“, a pattern where the dependency is injected as an external argument to a class constructor. A good explanation of Dependency Injection can be found on the blog of Martin Fowler. Even though the concept is great as you decouple the responsabilities, you still inject framework or library components and use them in your business classes. Well, this is not entirely true as they are injecting “interfaces” instead of “concrete classes”. By injecting an interface you basically state that a class that’s being injected is following the contract you require for your business logic.
One of the PHP provided interfaces is Countable. This allows you to offer the functionality to count elements in your class.
/** * Classes implementing Countable can be used with the * count function. * * @link https://php.net/manual/en/class.countable.php */ interface Countable { /** * Count elements of an object * @link https://php.net/manual/en/countable.count.php * @return int The custom count as an integer. * * * The return value is cast to an integer. * @since 5.1.0 */ public function count(); }
This interface is not implementing a concrete “count()” functionality, merely describing that when you want to offer a counting functionality in your class, you can implement this interface as blueprint and make your class be counted on like the following:
class BookCollection implements \Iterator, \Countable { /** * @var int The number of elements found in this collection */ protected $count; /** * @inheritDoc */ public function count() { return $this->count; } /* implementation of \Iterator interface omitted */ }
The beauty is that you can implement a multitude of interfaces in your class like in the above example where we implement both \Countable and \Iterator interface. Focussing back to our “count” functionality, in order to have our class returning an element count, we just need to call:
echo count($bookCollection);
Another good example of an interface is the PSR/Log/LoggerInterface where the PHP-FIG has created a standard how to use logging within an application, a contract of methods you should implement in your library or framework when you offer logging. This type of userland interfaces are useful if you’re building tools, applications, libraries or frameworks and you want to be consitent with some agreed standards.
When you have all these interfaces coming from frameworks, libraries and standardization bodies, why can’t we have them talk to our own interfaces through some mapper or translator. This way we can build our business logic completely on its own. Integration of a database, logger or mailer requires the framework injects its interfaces into our own interface classes.
Your business logic needs to talk with some outside systems like databases, caches, loggers, web services, etc… . This is all good since we can use our framework of choice to provide us with these functionalities or we use a 3rd-party library for a particular service. So when we build our business logic, we have them interact with our own interfaces instead. We can make use of dependency injection or lazy loading for getting the services up and running, but instead of implementing the real functionality to interact with let’s say a database, we instead provide an interface instead.
By using a combination of interfaces and services, we can isolate our business logic to focus on what is important for the business. The framework or library provides functionality to display our business logic (web site) or to process our logic (web service). The required components from the framework like an ORM or logging functionality can be injected into our own logic using the interfaces. If the framework or library adheres to the PSR standards we already mentioned earlier it’s a double win as we don’t need to provide mapping between our interface and the framework’s interface.
When building applications like this or when you want to adapt existing applications to use this way of operations, it’s mandatory to ensure the quality of your own business logic. Making sure that you adhere 100% to coding standards and having a 100% code coverage with your unit tests, makes the logic work flawlessly. Any deviation from these high requirements will result in a workflow where frameworks or libraries become part of the business logic and make it hard to switch or replace later on.
PHPNW
Come and join us in Manchester (UK) for the PHPNW Conference 2016 between September 30 and October 2. I will be giving my presentation “Decouple Your Framework Now, Thank Me Later” there. Make sure you save yourself a seat and buy tickets at their website.
PHP[World]
If you’re on the other side of Atlantic Ocean, no dispair. I will be giving the same presentation at PHP[World] in Washington D.C. (US) between November 14 and 17. Make sure you get your tickets!
2 Comments
Romans · 14/08/2016 at 02:48
Hey, Michelangelo. I’m a PHP developer from Hertfordshire. I’ve been working on a business-logic and persistence framework (http://git.io/ad) that focuses on business logic implementation and propose a similar idea: use your MVC framework of choice for presentation and routing, but use a different framework for business logic implementation. I would be curious to know your thoughts on my project. If you like it, maybe you could write about your experience with it?
Michelangelo van Dam · 14/08/2016 at 08:21
Hey Romans,
I’ve taken a look at your agiletoolkit and it sure looks like a valuable framework for business model mapping.
I need to find some time to play with it to figure out how and where it can fit in the decouple lifecycle as it is a framework on its own which is exactly what I want to step away from.
But thanks for pointing it out and I will surely make a reference of it in my presentation.
Comments are closed.