blog

  • Home
  • blog
  • Achieving Modular Architecture with Forwarding Decorators

Achieving Modular Architecture with Forwarding Decorators

As your web application becomes larger, you certainly start to think more about designing a flexible, modular architecture which is meant to allow for a high amount of extensibility. There are lots of ways to implement such architecture, and all of them circle around the fundamental principles: separation of concerns, self-sufficiency, composability of the parts of an app.

There is one approach which is rarely seen in PHP software but can be effectively implemented — it involves using native inheritance to provide manageable patching of the software code; we call it the Forwarding Decorator.

Picture for attention

Introduction To The Concept

In this article, we are going to observe the implementation of the Forwarding Decorator approach and its pros/cons. You can see the working demo application at this GitHub repository. Also, we’ll compare this approach to other well-known ones such as hooks, and code patching.

The main idea is to treat each class as a service and modify that service by extending it and reversing the inheritance chain through code compilation. If we build the system around that idea, any module will be able to contain special classes (they will be marked somehow to separate from the usual classes), which can inherit from any other class and will be used anywhere instead of the original object.

Comparison of the original and compiled classes

That’s why it is called Forwarding decorators: they wrap around the original implementation and forward the modified variant to the forefront to be used instead.

The advantages of such an approach are obvious:

  • modules can extend almost any part of the system, any class, any method; you don’t have to plan extension points in advance.
  • multiple modules can modify a single subsystem simultaneously.
  • subsystems are loosely coupled and can be upgraded separately.
  • extension system is based on the familiar inheritance approach.
  • you can control extensibility by making private methods and final classes.

With great power comes great responsibility, so the drawbacks are:

  • you would have to implement some sort of compiler system (more about that later)
  • module developers have to comply with the public interface of the subsystems and not violate the Liskov substitution principle; otherwise other modules will break the system.
  • you will have to be extremely cautious when modifying the public interface of the subsystems. The existing modules will certainly break and have to be adapted to the changes.
  • extra compiler complicates the debugging process: you can no longer run XDebug on the original code, any code change should be followed by running the compiler (although that can be mitigated, even the XDebug problem)

How Can This System Be Used?

The example would be like this:

class Foo {
    public function bar() {
        echo 'baz';
    }
}
namespace Module1;

/**
 * This is the modifier class and it is marked by DecoratorInterface
 */
class ModifiedFoo extends Foo implements DecoratorInterface {
    public function bar() {
        parent::bar();        
        echo ' modified';
    }
}
// ... somewhere in the app code

$object = new Foo();
$object->bar(); // will echo 'baz modified'

How can that be possible?

Achieving this would involve some magic. We have to preprocess this code and compile some intermediate classes with the reversed inheritance graph, so the original class would extend the module decorator like this:

Continue reading %Achieving Modular Architecture with Forwarding Decorators%

LEAVE A REPLY