This is the first post in a series describing a business component architecture using Spring 3.0/3.1 features like Java based configuration, the environment abstraction, bean definition profiles and property sources. Check here for part two on resources and here for part three on properties.
Today we’re gonna step down from the cloud(s) to a rather traditional, old fashioned software development environment: a big company with several hundred application developers working in different IT departments, not for developing a product, but for supporting the business of the company. They are each responsible for a certain aspect of the overall functionality. Every IT department is producing components that can be used by other departments. Those business components shall be usable in different contexts, for example online and batch. To be able to react to future requirements, the usability should be restricted the least possible. Vendor-lock-in shall be avoided. A very important aspect is a good testability.
How could such a business component architecture look like?
A business component consists of a public interface describing the contract the component is offering and a hidden implementation.
Technically the public part is a collection of interfaces, DTO classes and exceptions, while the hidden part includes the implementation of the interfaces. Of course the business logic can be divided in sub components.
To keep the example clear we’ll have two business components containing one service each. The first one is the PartnerService and a DTO:
1public interface PartnerService {
2
3 public Partner getPartner(long id);
4
5}
6
7public class Partner {
8
9 private long id;
10 private String name;
11
12 // getters and setters omitted for readability
13
14}
The second one is the CashingService with a DTO:
1public interface CashingService {
2
3 public void doBooking(BookingInfo bookingInfo);
4
5}
6
7public class BookingInfo {
8
9 private long partnerId;
10 private BigDecimal amount;
11 private String subject;
12
13 // getters and setters omitted for readability
14
15}
This was the public part of the business components. The hidden part, the implementation of the services, is one class each:
1public class PartnerServiceImpl implements PartnerService {
2
3 @Override
4 public Partner getPartner(long id) {
5 Partner partner = null;
6 // TODO do something to get partner
7 return partner;
8 }
9
10}
The implementation of the CashingService has a dependency on the PartnerService injected through the constructor.
1public class CashingServiceImpl implements CashingService {
2
3 private PartnerService partnerService;
4
5 public CashingServiceImpl(PartnerService partnerService) {
6 this.partnerService = partnerService;
7 }
8
9 @Override
10 public void doBooking(BookingInfo bookingInfo) {
11 // TODO validate bookingInfo
12 Partner partner = partnerService.getPartner(bookingInfo.getPartnerId());
13 // TODO use partner to do the booking
14 }
15
16}
Dependency structure interface and implementation
We use Maven for build and dependency management.
We divide interface and implementation of a business component in two separate projects. Alternatively we can have one project producing two artifacts, that would be a similar solution. In the following I will always refer to the different projects approach. Well, an implementation project is always depending on its own interface project, but can depend on as many other interface projects as needed. In the example the cashing implementation project depends on the partner interface project. The important thing is that implementation projects never depend on other implementation projects, even not transitive, and it can never happen, that the developer of one business component accidentally uses implementation details of another business component. Every business component defines itself only through the interface, implementation details can be replaced at any time. Business logic can be tested easily by unit tests.
Now we have two projects with POJOs containing business logic implementation and interfaces. Still missing is the configuration connecting the components via dependency injection. I suggest Spring’s Java based configuration. For the partner business component such a configuration looks like this:
1@Configuration
2public class PartnerConfig {
3
4 @Bean
5 public PartnerService partnerService() {
6 return new PartnerServiceImpl();
7 }
8
9}
This configuration gets its own project having a dependency on the implementation project. In this way we separate configuration and infrastructure strongly from business logic, for example we have no dependency on Spring in the interface and the implementation project. The configuration of the cashing component has a dependency on the configuration project of the partner business component:
1@Configuration
2@Import(PartnerConfig.class)
3public class CashingConfig {
4
5 @Autowired
6 private PartnerConfig partnerConfig;
7
8 @Bean
9 public CashingService cashingService() {
10 return new CashingServiceImpl(partnerConfig.partnerService());
11 }
12
13}
Complete dependency structure including configuration
The CashingConfig imports the PartnerConfig, which is used to inject the PartnerService into the CashingServiceImpl.
Though my Javamagazin article already mentions advantages of this type of configuration I want to point out the most important features here, especially for a distributed development environment:
- Navigation in Spring configurations (even over jar boundaries)
- Locating configuration files in bound jars
- Detecting usage of a certain class or interface in configuration files
Being able to navigate through the configuration with standard IDE functionality makes it easy to understand it. In the example it’s one click from the definition of the CashingService to the definition of the PartnerService, even if it’s in a bound jar and not as source in the workspace. That’s not possible in XML.
The configuration file being a Java class makes it possible to find it via “Open Type”. Being an XML file it cannot be found via “Open Resource”.
Again, no problem in Java, even in bound jars. With XML at least not possible in jars in the classpath.
Explicit configuration with JavaConfig supports understandability and traceability, key features for error prevention, bugfixing and maintainability.
Using a business component
We got the configuration of a business component in Spring’s JavaConfig. To use the component we need an instantiated ApplicationContext with the configuration bound to it.
So, what are our options? It’s easy when the application wanting to use the business component itself is a Spring application. Then we can import the configuration into the existing configuration. For example for binding the cashing business component into the application we just need to import the CashingConfig class into the existing ApplicationContext. All configurations CashingConfig is depending on get imported automatically.
If that’s not the case we need to have an infrastructure unit managing the ApplicationContext and offering the services to external clients. It could be a web application offering restful services. It could be an EJB accessing the ApplicationContext. Or it could be an application listening to a queue. There are a lot of options.
Conclusion
The business component architecture presented here divides the necessary parts of a business component in three projects / artifacts:
– interface
– implementation
– configuration
Through the defined dependencies between the projects / artifacts we achieve a strong separation of public interface and hidden implementation and business logic and infrastructure. The usage of explicit, Java based configuration supports an easy handling in every IDE and understandability and traceability which leads to maintainability. Through consequent application of dependency injection we achieve an easy testability. The fact that implementation projects may not reference other implementation projects enforces dependency injection. Last, but not least: a business component does not need a certain runtime environment, it can be used in different functional and technical contexts.
What now?
Of course there are still a lot of open questions, for example handling properties, resources and environment specific configurations. Spring 3.1’s environment abstraction offers new possibilities here, I will talk about them in follow-up blog posts:
A business component architecture with Spring 3.0/3.1 – Part 2: Resources
A business component architecture with Spring 3.0/3.1 – Part 3: Properties
One final word regarding explicit and implicit configuration
Definition explicit configuration: Dependency injection between components is configured explicitly via XML snippets or Java code.
Definition implicit configuration: Dependency injection between components is done either by conventions or by classpath scanning and autowiring with annotations.
Definition explicit / implicit configuration
Convention over configuration is the talk of the town, and through all the recent XML bashing explicit configuration has become pretty uncool. Nevertheless I present an approach here with explicit configuration playing an important part. Why?
- The preconditions
- Explicit configuration does not mean XML
- This is enterprise, coolness is not important
We have hundreds of stakeholders, different IT business departments, central architecture departments and operations. The application’s configuration MUST be easy to understand and follow. And explicit configuration is easier to follow then automatic scanning and instantiation of components in the classpath. And, being honest, how much time does it take to do a configuration for a component? Two minutes?
There is no XML in my concept, Spring’s Java based configuration has a lot to offer. Honestly, I wouldn’t do explicit configuration in XML anymore.
I don’t present the concept here because I think it’s some cool hype thing, but because I think it works. And that’s still the most important thing in software development.
More articles
fromTobias Flohre
Your job at codecentric?
Jobs
Agile Developer und Consultant (w/d/m)
Alle Standorte
More articles in this subject area
Discover exciting further topics and let the codecentric world inspire you.
Gemeinsam bessere Projekte umsetzen.
Wir helfen deinem Unternehmen.
Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.
Hilf uns, noch besser zu werden.
Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.
Blog author
Tobias Flohre
Senior Software Developer
Do you still have questions? Just send me a message.
Do you still have questions? Just send me a message.