Packages should be designed to perform a single function well.
Ideally this means that they should be highly modular and different packages should be able to be 'plugged-in' simply by ensuring that they share a common interface.
However, I'm unsure how to do this without creating some kind of dependency between the packages or creating ugly 'integration classes' or a 'common interfaces' package.
An Example Concept
I don't want to get too bogged down in domain-level symantics, so I'll use the simple, though slightly contrived, example of an event logger.
Does XYZ and allows for a custom logging object to be provided.
To ensure that the logger is valid, Package A contains a
PackageA\Log interface. When a logging object is provided to the
classX constructor, it throws an exception if the logger object does not implement
Does ABC and allows for a custom logging object to be provided.
To ensure that the logger is valid, Package B contains a
PackageB\Log interface. When a logging object is provided to the
classA constructor, it throws an exception if the logger object does not implement
A unified logger that provides logging for Package A and Package B... et al.
Log object that is built to provide a generic logger for all packages in the application.
Current Sub-optimal Ideas:
1. Implement neither of the interfaces in the class
PackageC\Log but extend the class for each interface variation (e.g.
Problem: Requires more maintenance and means that
PackageC will have to be modified for every new package that it interfaces with.
2. Implement the interfaces
PackageB\Log directly in the class
PackageC is re-used in another project, errors will be thrown if either
PackageB are not present.
3. Create a Common Interfaces package and have all packages implement/require those interfaces for their public interfaces.
Problem: Massively impractical, would only work for integrating packages that you created; no third party interaction.
PackageC\Log fulfill the requirements of both
In reality this question is usually more complex since the interfaces required by
PackageB are probably not the same. So is option 1 (defined above) the only way to solve this? i.e. the package that implements the interface has to write integration classes?