How to use Layered architecture of spring and still follow object oriented structure?

In nutshell

Layered architecture will just ease the code maintainability and consistence when it becomes huge and complex.

The fact to remember is that to do a proper software design before doing the implementation.

  • Encapsulation - Business logic specific for a domain model should go inside it.
  • Abstraction - Segregate the interfaces according to the grouping of services while writing the common business logic in the abstraction.
  • Inheritance - Use when you are drafting your domain objects
  • Polymorphism - Along with inheritance when you want to change the business logic of child models.

In detail

Below I'm trying my best to provide an example of an ERP application for this discussion. Hope an ERP is a sufficient big project to view the business logic complexity.

The below description is for any developer who need an idea to understand and make use of layered project structure in Spring (or any other framework).

But please note these are not rules to be followed but the best practices to be utilized. :)


1. Data Access Layer - Model /Domain Objects

This contains the mapping of actual tables to classes.

In an ERP example, this is where you get the models: CustomerOrder, CustomerOrderLine

This also contains the en-capsuled logic of child domain objects and the domain logic of self. For example, the CustomerOrderLine is a child of CustomerOrder. The child cannot exists without the parent. So the parent will have the full control of building the childs within it. ie Business logic encapsulation.. ie: Add CustomerOrderLine, RemoveCustomerOrderLine etc.. And when it comes to self domain logic, ApproveCustomerOrder, RejectCustomerOrder etc..


2. Data Access Layer - Repository

This contains nothing but simple CRUD to database with SELECT, INSERT, UPDATE and DELETE SQLs. You can use repository pattern in Spring along with Spring Data JPA.

Key note: do not write any complex logic in this layer unless your logic is highly data intensive

In that case you might have to write one or more functions to do complex query statements. (Preferably in JPQL)

In an ERP example, this is the place you write logic for GetCustomerOrders, GetCustomerOrderByCustomer, GetCustomerOrderLines, GetOrderByStatus

In simple terms this layer defines how the application will communicate with the outside entities such as Database.


3. Service Layer

This is the place where you should put your complex business logic which involved Multiple unconnected (not child - parent) domain models. These will be reused in Web Controllers and Rest API Controllers.

So to maintain the consistence and to implement the security, I would prefer all the business logic even which were written in the domain models gets wrapped up at this layer.

In the ERP exmple this is the place you write the logic or wrap the logic which is written in Domain Model. For example CreateCustomerOrder, ListCustomerOrder, ApproveCustomerOrderLine, ReleaseCustomerOrder,...

In case if these logic should get executed along with other model logics, then those should be called at sequence within service layer as well. For example.

Misc Examples on complex business logic

If you want to create a Purchase Order for your supplier, when the Customer Order is released.

Then, this can be done by creating a service called SupplyChainService binded using Spring AOP to the CustomerOrderService.ReleaseCustomerOrder. In microservice design this can be done by a event published by Supply chain domain microservice to a queue and get consumed by Customer Order domain microservice


4. Controllers

Controllers can be categorized in two, namely: Web Controllers, and REST Controllers. No business logic should be implemented in this layer because the same logic can be required to call in Web as well as in API level.

In the ERP system, this is the place where you will write the controller for your customer order form to enter data and save it to create a new customer order.

This will be the place you will also create a API controller like REST to create the customer order via a mobile application or from a windows client.

Thanks to the SO community who showed me areas that I didn't cover on OOP principles in this answer

Edit


This is an answer when microservice is not too mainstream. Though it answered the OOP concepts, also look into CQRS based design as it's more common in modern micro service based architecture. Either way, you can incorporate OOP concepts regardless of what software architectural pattern you use.


Spring application design and OOD are not mutually exclusive.

The typical Spring (MVC) application has the following structure:

  1. One or more @Controllerclasses. These are the entry points of your application. They should not contain any business logic. Despite the name I identify them as the V in MVC (Model-View-Controller)
  2. One or more @Service classes. This is where you develop your business logic (BL). One of the benefit in putting your BL here is that it can be reused by multiple controllers (by a @Controller and by a @RestController for example). They are the C in MVC.
  3. One or more @Repository classes, where you implement your persistence layer (database, in-memory, whatever...). Additionally you can implement a set of @Components classes that describe your domain objects. These are the M in MVC.
  4. Other classes that you can't map in the MVC pattern, like @Configuration, @ControllerAdvice and other Spring configuration/management classes.

While designing each of these objects, you can follow whatever OOD approach you like.

In the OOD example you mentioned, I would design my application like this:

  1. One view (@Controller) for each actor
  2. One @Service for each use case
  3. One @Repository and one @Component for each domain class

EDIT: you can find an example project I wrote for university here. It implements what I explained in the last three points with an additional layer (between @Controller and @Service) because there was a requirement to minimize dependencies on the C layer. The concepts still apply.


I think, you are asking a wrong question here. Layered architecture is inherently not object-oriented, and therefore, while using (some of) the object-oriented practices with it would be possible or even advisable, it should not by itself be the goal. It is akin to asking how do I use best truck driving practices to ride a bike.

Why am I saying that layered architecture is not object oriented? Well, as we know, there are three principles that distinguish object-oriented design: encapsulation, inheritance and polymorphism.

While the last two may or may not be present, depending on your design choices, layered architecture is, pretty much, the opposite of encapsulation: by nature of this approach, you explicitly separate your data ("DTOs") from your logic ("services").

Don't get me wrong, the fact that this approach isn't object-oriented doesn't mean that there is anything wrong with it. And vice versa, "object-oriented" isn't synonymous with "good", it's one of many tools in a programmer's toolbox, and, as with any tool, is better suited to solving some problems than others.

Layered architecture is a good design pattern, that can be successfully used to solve many real-life engineering problems. It has it's own set of best practices that can and should be used, and while that set may have some intersections with its counterpart from OOP, the two are certainly not equivalent.