In the first part of this series I handled the module API. I showed that it is a protective shield around the module that keeps it from being bound to other modules. In this part I will put it to work and show how it deals with its dependencies: the other modules on which it depends.
The RelationAPI that I introduced in part 1 will need some functions that are outside of its direct control. For example, when you call hasReadAccess($relationId, $Object), the API needs to call the PrivilegeAPI to get the information. How does it get access to that other API?
The PrivilegeAPI is what's called a dependency of the RelationAPI. The RelationAPI cannot just create a PrivilegeAPI object in order to use it. The reason for this is has to do with testing. If you call the function hasReadAccess from within a unit test, you don't want it to actually call the functions of the PrivilegeAPI. If it did, the unit test of hasReadAccess would be executing code from the PrivilegeAPI, and this is undesirable for the following reasons.
- If there's a bug in PrivilegeAPI it will mess up the test for hasReadAccess as well.
- The PrivilegeAPI functions may perform some code that makes a unit test impossible, like calling a database, or sending out an e-mail.
- It is also slower to execute the unit tests.
Apart from these reasons, there is also a functional reason: by instantiating the dependency classes from within the code, the dependencies will become hard-wired. The API can only be used with the dependencies it itself dictates. It is more flexible to pass external dependencies to the API from outside. This way, you yourself select the implementation of the dependency.
Thus, for testing and functional purposes, we inject the PrivilegeAPI into the RelationAPI as a dependency. We use constructor injection for this.
This is what it looks like:
$RelationAPI = new RelationAPI(new PrivilegeAPI());
This is a solution, but it causes another problem. Everywhere the RelationAPI is needed, all dependent API's need to be created first. And these may change over time, and these dependent APIs may have dependencies themselves.
To solve this problem we created the API Factory. Each module has a single factory class that creates API objects and passes its dependencies to it. Like this:
* @return RelationAPI
public static function getRelationAPI()
return new RelationAPI(new PrivilegeAPI());
In order to use the RelationAPI from another module, it needs to create it like this:
$RelationAPI = RelationApiFactory::getRelationAPI();
The RelationAPI then has all functions of the PrivilegeAPI at its disposal. The function hasReadAccess may look like this:
public function hasReadAccess($relationId, $Object)
return $this->PrivilegeAPI->getAccess($relationId, 'read', $Object);
This technique is created in the spirit of the Ports-and-Adapters architecture. In view of this architecture, the APIs take the role of the primary ports of the module and the API Factory creates the default secondary adapters that are plugged in module's secondary ports. A consequence of this setup is that the default dependencies could be replaced by other ones. And this is in fact what we do when we test the API. We replace the dependencies with mock objects.
The API factory of a module allows a module to disassociate the dependencies from the real code. It does this in a way that avoids code duplication. Also, if forms a single point of entry to all the functionality of a module. It says: "Start here!"
The Sugar Factory photo can be found here.