2

Currently our service is implemented using a multilayer architecture dividing the whole service into three:

  • API
  • Business
  • Persistence

However this introduces a lot of redundancy within our system. A common adage in the industry is "DRY" (Don't Repeat Yourself). The redundancy has increased the development time, and made the system more fragile and cluttered our code with "copy" methods.

To give a better idea, say we have a Person service. This would require the following:

  • Person entity - JPA annotated class for ORM
  • Repository service request - contains field values to be persisted of the Person domain object with additional persistence options
  • Repository service response - contains field values of the Person entity
  • Person - class with business logic, domain fields and computed fields
  • Domain service request - contains field values of Person resource and additional business options
  • Domain service response - contains field values of Person business object excluding those that shouldn't be visible to API users
  • Person resource - class representing what should be viewable to the API users

And things get worse when taking nested objects into consideration.

The current design facilitates difference between concerns (business, API, persistence), however:

  • Currently, the differences are very small. This is causing us to have very similar classes with only minor differences
  • Services returning service response objects with fields instead of just the objects itself hampers other services from depending on other services

Questions:

  • Is it worth it to go through with this design?
  • What are our alternatives?
  • What could we change to improve our situation?
1

On first sight, I find your architecture overly complex.

What is the purpose of having a Person(@Entity), a ServiceRequest, ServiceResponse, DomainServiceRequest, DomainServiceResponse besides sleeping good at night with abstractions for the abstractions sake?

You are drowning little puppies here

Within the Backend of your application, using only the Person POJO (=Entity) would be sufficient.

In a typical MVC-style (web-)application you have a Rest-Controller where all requests from the "outside" land. Depending on what action is required, you could make a request to the repository for querying data ( e.g. GET /api/users/1 you call personRepository.findOne(1) and return the result ) or you ask a service to manipulate data or do something more complex than just retrieving data.

Then you have a servicelayer, where your business logic sits and your POJOS are manipulated. The changes are then persisted in the Repository-Layer (eg. personRepository.save(person)).

This is a rather pragmatic than orthodox architecture: querying data in the Controller is not 100% separation of concerns - on the other hand, what would be won, when the query is encapsulated in the service-layer?

You could even go further and organizing your code in such a way, that you have a Package Person which contains the Controller, the Service and the Repository, which makes it easy later on cutting your app up into separate services (if needed).

Besides the internal use of the POJO/Model, perhaps it makes sense to use another modelling object to transfer over the wire. It's dependend on the use case.

The redundancy has increased the development time, and made the system more fragile and cluttered our code with "copy" methods.

That is a clear sign, that your architecture needs some revision.

Your design is a typical onion-design: it has many layers and makes you cry.

1

If your personal opinion is already that it is too much work, then yes, look at ways to simplify the design by merging or omitting certain classes.

Some things to consider:

  • Merge the entity and the business classes. If it can be done in a 'clean' way with your ORM framework.
  • Be adaptable. For some crazy classes you may benefit from all this infrastructure but keep simple cases simple. If a Person has an Ethnic property that has admin screens which are pretty much taking the UI fields and updating them in the database, do not bother with all the overhead. A DTO, an Entity and a generic Repository may do the trick.
  • Request/Response/resource classes: You have different options here
    • Allow the Repository service and Domain service to be more intimate: use the same Request/Response classes for this boundary.
    • Repository service request: If you extract all the options to its own class, you might no longer need these. (Especially useful if you would only end up with one/a few identical options classes) - similar option classes could be used instead of special domain service request objects.
    • Domain service response: These could already be returned directly from the Repository or the Domain layer in those cases that the layers below don't actually do anything with the fields that are stripped off.
  • Keep the repositories down by using aggregated roots for nested objects. ex: The Person repository automatically updates/inserts/deletes all Hobbys instead of having its own Hobby Repository.

Keep in mind that if you have large compositions of objects all this serializing/deserializing between layers might start taking noticeable time.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.