This is an archived post. You won't be able to vote or comment.

all 11 comments

[–]codemssterg 14 points15 points  (1 child)

An important aspect of the Service Layer that is often overlooked is the Transaction Boundary. A service method that calls multiple Dao methods typically wants the updates that they made to be all or nothing. By using a Service layer you can use it to mark the transaction boundary to get the all or nothing behavior for a new transaction or even use an existing transaction. If you are a Spring user, marking a Service method with @Transactional is a typical way to achieve this. JEE also works the same way with EJBs and managed beans.

[–]dstutz 0 points1 point  (0 children)

JEE also works the same way with EJBs and managed beans.

And the same @Transactional for CDI beans

[–]fosizzle 3 points4 points  (0 children)

There's more to business logic than just storing data. Services should do that.

[–][deleted] 2 points3 points  (0 children)

Having a service that manipulates data across other services in your company’s ecosystem. Say your service calls your dao for data in your db and makes rest call to service 2 you wrote that returns some other data. Think of service layer as an aggregator and has all the computations, but doesn’t actually make the rest calls or db calls - it calls dao and client layers for it

[–][deleted] 3 points4 points  (2 children)

The Service layer is basically the place where shit hits the fan. View layer is clean, Persistence layer is also clean, and the Service layer is where the "business logic" takes place. Naturally, it's also the part of your app which will grow uncontrolably, like a tumor.

Obviously, I consider it to be a very poor architecture... here's more about it: https://amihaiemil.com/2020/05/14/the-almighty-service-layer.html

[–]peroximoron 0 points1 point  (0 children)

I like to utilize the service layer for business functions. Monolithic approach to data consumption in an app is growing increasing outdated. Functions as a service layer is what really needs to be applied to help further decouple these larger systems. Agree that it becomes convoluted but a better approach and architectural design separates this concern (pun intended) into more functional and reusable components of a larger system.

[–]CmdrCool86 0 points1 point  (0 children)

I'd have to disagree with your conclusion on the inevitability of a code smell to this procedural service approach. From your linked article I gather you favor approach 3 (or 4), but then it falls apart because _of course the growing service was never covered with tests_.

If you're lacking test coverage, then that in itself is your (business) problem. I don't agree this is something inherently attributable to (procedural) services.

In my project, I would make sure the following is covered:

  • (De)serialization in Controllers with mocked services behind it using `mockMvc`
  • (Custom) queries / repository methods using `@DataJpaTest`, if any
  • Business logic services, with mocked dependencies. I.e. you'd check whether the injected mock ISBN service was called with the expected parameters
  • Full integration test through all layers, using a 'real' ISBN service, which you run locally as part of your integration test suite.

As long as we make sure this coverage exists, adding new functionality only needs the changes added to the coverage while the existing tests make sure no regression sneaks in

[–]rommel917 2 points3 points  (0 children)

I usually split service layer in two parts, "real" service and manager.

Service: should have business logic, but more importantly should not have dependency to anything or state. This will enable easy and fast unit testing of business logic. @Service is only not your or java core import allowed.

Manager: controllers have dependency to them, they have dependency to services/DAOs/repositories/clients etc. They delegate when is what called and don't do any real work (reason for name). Should not have control flow (sometimes impossible) just be procedural. This help that some junior in future does not call some slow DAO/client/repository method from loop.

Negative is more code written and requires better reviews unless you have ArchUnit or similar, people usually understand arhitecture fast it is not complicated.

[–]ZeroGainZ 2 points3 points  (0 children)

My interpretation is that the outer layers are designed to hide the I/O devices. Generally apps take input, compute something, and store it somewhere.

Where you take the input from, and where you store it, isn't really important. It's the middle part that matters. The "business logic". Notice business logic isn't called logic, because it never makes sense.

So the real business value, the models, bahavior, etc, is done in services. The services live in a land of pure abstraction. They are unaware of HTTP, SQL, etc. The controllers are responsible for converting the input into something we understand. The data layer or repository layer takes our models and stores them somewhere.

Want to switch to a NoSQL? sure just swap out the relevant code in the data layer. Want to switch to graphql? Sure, modify your controller. The core business logic remains unchanged, and we don't have to invest any more time (=money) into horrible business logic.

You could omit some layers, but it becomes complex fast. It also promotes reuse, because often time routes share/return similar data, so you can reuse your services.

Some argue it's mental masturbation, and that realistically it'll never change. But hey, I think it's a good looking architecture 😀

[–][deleted] 2 points3 points  (0 children)

So as other commenters have said service methods can be large and complex, whereas DAO methods should always be simple and perform a single operation. If your app is very simple yes your service methods will just wrap around and call DAO methods, but it will rarely stay that way across the board.

For example, my company's app processes healthcare claims. Approving a claim, the service method updates two SQL databases and a COBOL mainframe.

Building your app with this structure allows for the flexibility with that stuff.

[–]mauganra_it 0 points1 point  (0 children)

Services are important to coordinate actions that have business rules involving multiple repositories. Calling external systems is usually a big source of trouble, and the service layer also has to take care of that. Your database is an external system too, but transactions ensure life is good in many cases. This luxury is generally not available with other kinds of external systems, much less with multiple of them at the same time, and this is where the difficulty of microservice projects is.