In the last chapter, we explored Association, the fundamental "uses-a" relationship that connects independent objects. We learned that in an association, objects have their own lifecycles.
But what happens when the relationship is a bit tighter? What if one class represents a "whole" and another represents a "part" of that whole?
Think of a university department and its professors, a team and its players, or a playlist and its songs.
This is where Aggregation comes in. It’s a specialized, stronger form of association that models a "whole-part" relationship.
Aggregation is a weaker form of the whole–part relationship where one class (the “whole”) contains references to other class objects (the “parts”), but the parts can exist independently of the whole.
It’s often described as a “has-a” relationship with loose ownership.
If a class contains other classes for logical grouping only without lifecycle ownership, it is an aggregation.
Let’s consider a university context:
This relationship models Aggregation—the department and professors are linked, but their lifecycles are not tightly coupled.
In UML class diagrams, aggregation is represented by a hollow diamond (◊) on the “whole” side of the relationship.
The diamond connects to the class that contains or references the other objects.
This diagram clearly reads: "Team has an aggregation relationship with Developer."
Here, the hollow diamond (◊) at the Team side signifies aggregation, and the solid line points to the Developer class, representing the referenced objects.
Let’s model a real-world aggregation: The relationship between a university Department and its Professors.
A department "has" professors, but the professors are independent entities. If the department is restructured or closed, the professors (as university employees) still exist and can be assigned to other departments. The department does not own the lifecycle of the professors.
Department groups Professor objects.Department class.If you delete the csDept object, the professors still exist in memory and could be reassigned to another department. That’s aggregation in action.
Choosing aggregation in your design has significant benefits for software architecture:
Developer or a Microservice) are independent and can be reused across multiple "whole" objects (Teams or ApiGateways).Team class without affecting the Developer class, and vice versa.Team class has a method createNewDeveloper(), creating and destroying Developer objects internally. This creates tight coupling, making it behave like composition.Team class holds a reference to Developer instances that are created elsewhere and passed to it. This is standard aggregation.Team's dependencies (the list of Developers) are provided via its constructor or a setter method (Dependency Injection). This is the most flexible approach, promoting high modularity and making the Team class easy to test with mock Developer objects.In this chapter, we explored aggregation that allows objects to collaborate while keeping their lifecycles independent.
But sometimes, relationships are tighter than that. Some objects are owned by others, created and destroyed together. In these cases, the whole truly controls the part’s lifecycle.
This stronger relationship is known as Composition.
Lets explore that in the next chapter.