Chapter 12: Components
"Components are the units of deployment. They are the smallest entities that can be deployed as part of a system."
๐ For New Learnersโ
What Is a Component?โ
A component is the smallest deployable unit:
- In Java: a JAR file
- In .NET: a DLL
- In compiled C: a shared library (.so / .dll)
- In JavaScript: an npm package
Components are what you ship. They can be combined into a single executable or deployed as independently upgradeable pieces of a larger system.
A Brief History: Why Components Matterโ
In the early days of programming (1950sโ60s), programs were tiny and loaded directly into memory starting at a fixed address. Functions from libraries were copied directly into the binary โ if you needed a sorting routine, the whole routine was embedded in your program.
As programs grew, this became painful. Memory was scarce. You didn't want multiple programs each carrying their own copy of the same library. The solution: relocatable binaries and linkers.
Linkers allowed programmers to keep library functions in separate files. At link time, the linker stitched them together into a single executable. This was slow (linking could take hours), but programs could share library code.
Dynamic linking (shared libraries, loaded at runtime) solved the remaining issues: multiple programs share one in-memory copy of a library. Change the library, all programs that use it benefit โ without relinking.
Modern Components: Still the Same Ideaโ
Today's JAR files, npm packages, and Maven artifacts are the direct descendants of these relocatable binaries. The principles are the same:
- Components can be developed independently
- Components can be tested independently
- Components can be deployed independently
- Components can be versioned independently
The chapters that follow (13 and 14) explain the principles that govern how components should be organized โ which classes belong together, and how components should depend on each other.
๐ฌ Senior Deep Diveโ
The Plugin Nature of Componentsโ
A key insight from the history: dynamic linking enables plugin architectures. At runtime, a program loads components it didn't know about at compile time. This is the foundation for:
- OSGi bundles in enterprise Java
- Spring Boot auto-configuration (discovers and wires
@AutoConfigurationclasses from JARs on the classpath) - Java ServiceLoader (discovers implementations of an interface from JARs)
Martin's point: component architecture is not a new idea โ it's the natural evolution of how programs have been organized since the 1950s. What's changed is the scale and the tooling.
Maven Multi-Module Architectureโ
In a Spring application, components map naturally to Maven modules:
my-ecommerce-app/
โโโ pom.xml (parent POM)
โโโ domain/ (JAR: pure domain โ no Spring)
โ โโโ src/main/java/
โ โโโ com/example/domain/
โโโ application/ (JAR: use cases โ depends on domain)
โโโ infrastructure/ (JAR: JPA, Kafka, etc. โ depends on domain)
โโโ web-adapter/ (JAR: Spring MVC โ depends on application)
โโโ app/ (JAR: Spring Boot main โ depends on all)
Each pom.xml declares only the components it needs. The domain module has no framework dependencies. The infrastructure module has JPA. The web-adapter has Spring MVC. This matches the Clean Architecture rings as Maven artifacts.
Component Granularity โ The Coming Challengeโ
Components being small and independently deployable is desirable โ but choosing the right granularity is difficult. Too fine: hundreds of tiny JARs, dependency hell, deployment complexity. Too coarse: one JAR that must always be deployed as a unit, no independent evolution.
The next two chapters (Component Cohesion and Component Coupling) provide the principles for making these decisions correctly.
Build Tools as Architecture Enforcementโ
Maven/Gradle module boundaries can enforce architectural constraints:
<!-- domain/pom.xml โ enforces: no Spring, no JPA allowed -->
<dependencies>
<!-- intentionally empty or only test-scope dependencies -->
</dependencies>
If a developer tries to add a Spring dependency to the domain module, the build fails. The architecture is machine-enforced, not just convention-enforced.
ArchUnit provides runtime enforcement:
@Test
void domainShouldNotDependOnSpring() {
JavaClasses classes = new ClassFileImporter().importPackages("com.example.domain");
noClasses().that().resideInAPackage("com.example.domain..")
.should().dependOnClassesThat().resideInAPackage("org.springframework..")
.check(classes);
}
Summaryโ
| Concept | Key Point |
|---|---|
| Component | Smallest deployable unit โ JAR, DLL, shared library |
| History | Evolved from relocatable binaries and linkers to modern package managers |
| Dynamic linking | Enables plugin architectures โ load implementations at runtime |
| Maven modules | Each Clean Architecture ring maps to a Maven module with controlled dependencies |
| Next chapters | Chapter 13: which classes belong in which component; Chapter 14: how components depend on each other |