Once upon a project there was a base class:
class abstract CommonLogic { protected CommonLogic(IFirstLowLevelService firstService) {} }
…that several developers wanted to subclass. They all believed in the inversion of control principle and took therefore all their dependencies as constructor parameters. With Autofac, a dependency injection framework at hand they ventured forth implementing many great subclasses. Some of these appeared like this:
class SpecificLogic : CommonLogic { public SpecificLogic( IHighLevelService highLevelService, IFirstLowLevelService firstService) : base(firstService) {} }
All was well and the number of variations of special logic classes grew and prospered.
Until one day a Senior Developer wanted to extend the base class with new logic requiring more dependencies. Now all the subclasses had to include the new dependencies in their constructors too, passing them on to the base class.
class abstract CommonLogic { protected CommonLogic( IFirstLowLevelService firstService, ISecondLowLevelService secondService, IThirdLowLevelService thirdService ) {} } class SpecificLogic : CommonLogic { public SpecificLogic( IHighLevelService highLevelService, IFirstLowLevelService firstService ISecondLowLevelService secondService, IThirdLowLevelService thirdService) : base(firstService, secondService, thirdService) {} }
The changes caused great disturbance throughout the Source Repository, affecting both production code and test code alike, delaying schedules and disheartening the developers.
Then a brave developer set out on a quest to free the Source from darkness. And he found the Aggregate Service, a pattern that promised to isolate the subclasses from future changes in the base class constructor:
class abstract CommonLogic { public interface IAggregateService { IFirstLowLevelService FirstService {get;} ISecondLowLevelService SecondService {get;} IThirdLowLevelService ThirdService {get;} } protected CommonLogic(IAggregateService aggregateService) {} } class SpecificLogic : CommonLogic { public SpecificLogic( IHighLevelService highLevelService, IAggregateService aggregateService) : base(aggregateService) {} }
Furthermore, by tapping into the power of Castle DynamicProxy2, the developer crafted a device that could dynamically generate aggregate services, completely removing the burden of implementing classes for each aggregate service interface.
Finally, the valiant adventurer approached the Senior Developer and presented the pattern and how it could free them from distress merely by aggregating constructor-injected dependencies into one dependency. Seeing its brilliance, the aggregate service pattern was implemented throughout the Source spreading light and joy to the team. Changes could now be made to the base class dependencies by merely changing the IAggregateService interface, and no change would have to be done to the subclasses ever again.
So great was the joy and relief that the Senior Developer declared:
The Aggregate Service belongs to all developers of the land and shall thus be contributed to Autofac.