Container role
The container creates objects, resolves dependencies, and manages lifecycle and scopes.
Dependency Injection can be done manually, but as a Java system grows, object creation, scopes, configuration, and cross-cutting wiring become harder to manage by hand. IoC containers solve that operational problem. Spring became the dominant Java mental model because it turned DI into a practical, large-scale development style.
Order Management systems accumulate repositories, gateways, policies, message publishers, schedulers, and controllers. An IoC container centralizes how those parts are assembled so the application code can focus on business behavior instead of manual composition logic.
An IoC container is not just a factory. It is a runtime composition system. It decides how objects are created, which implementation satisfies which dependency, when instances are reused, and how lifecycle hooks are managed. That is why container choice influences architecture, even when the domain model itself remains framework-agnostic.
Spring matters because it normalized constructor injection, component scanning, configuration classes, scopes, profiles, and integration wiring across the Java ecosystem. But the real architectural lesson is bigger than Spring: containers exist to support clean composition, not to justify hidden dependencies or container-aware domain objects.
The container creates objects, resolves dependencies, and manages lifecycle and scopes.
Spring made DI operationally convenient and connected it to configuration, web layers, transactions, and data access.
The container should support a good design, not become the design itself.
package org.javaomnibus.ecommerce.di;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class CommerceConfiguration {
@Bean
public PaymentGateway paymentGateway() {
return new StripePaymentGateway();
}
@Bean
public PlaceOrderService placeOrderService(
InventoryService inventoryService,
PaymentGateway paymentGateway,
NotificationService notificationService
) {
return new PlaceOrderService(inventoryService, paymentGateway, notificationService);
}
}
Configuration, wiring, profiles, transactions, web adapters, messaging adapters, and integration assembly are all natural container concerns.
Domain modeling, boundaries, policies, use cases, and aggregate behavior should stay understandable even outside the container.
Containers simplify large-scale wiring, but they can also hide architecture if teams overuse framework magic instead of preserving explicit boundaries and readable composition rules.