Question is a bit rhetorical. I've had an argument with my colleagues regarding the following pattern:
@Component
public class MetaService {
public static UserService userService;
public static GroupService groupService;
public static PermissionService permissionService;
// ...more fields
@Autowired
public MetaService(UserService userService,
GroupService groupService
PermissionService permissionService) {
MetaService.userService = userService;
MetaService.groupService = groupService;
MetaService.permissionService = permissionService;
}
}
Basically, MetaService
is an entry point/wrapper for multiple Spring beans @Service
classes. And there's one more similar MetaDao
wrapper for @Repository
beans class:
@Component
public class MetaDao {
public static UserDao userDao;
public static GroupDao groupDao;
public static PermissionDao permissionDao;
// ...more fields
@Autowired
public MetaDao(UserDao userDao,
GroupDao groupDao,
PermissionDao permissionDao) {
MetaDao.userDao = userDao;
MetaDao.groupDao = groupDao;
MetaDao.permissionDao = permissionDao;
}
}
The idea of having such a Meta-class is to reduce code base within @RestController
and @Service
classes, by providing concise API to call @Service
and @Repository
methods, for example:
@RestController
public class UserController {
@PostMapping
public UserCreateResponse createUser(UserCreateRequest request) {
MetaService.userService.createAndReturn(userEntity);
// other service calls
}
}
In other words, this is an alternative to having @Autowired
fields/constructors for each @Service
within each @Controller
etc. In my opinion this approach greatly reduces boilerplate repetitive code a lot.
Well, my colleagues are saying that: such an approach makes the code harder to understand/maintain since combining @Service
components in one place hides dependency model of other components which use them. In other words, if there were fields declared for UserController
class. It would've been easier to quickly find out which components UserController
depends upon. In addition, it's harder to mock such dependencies doing unit testing. And the most important
that such approach is super-anti-pattern. Whereas I can agree with mocking part, everything else seems ridiculous to me.
Question is, is this truly an anti-pattern in any way?
@Service annotates classes at the service layer. @Repository annotates classes at the persistence layer, which will act as a database repository.
The main purpose of a repository is to store a set of files, as well as the history of changes made to those files.
This is clearly an anti-pattern to me.
Why is there even a need for this? The UserController
needs the UserService
, why would it include a MetaService
containing a GroupService
it doesn't need?
Not sure what your MetaDAO
is supposed to do, bundle together objects that are in a certain relation? I highly doubt that's a proper implementation for most objects (can a user be in many groups? can he have several permissions?) What domain relevance is this supposed to serve? Or are they supposed to be @Repository
s?
The static members are public static
, but not final - breach of encapsulation. It's basically a badly implemented Singleton pattern.
The MetaService
might as well implement all three interfaces and maintain a relation to the repositories. No real need to put two layers of services between controller and repository if the actual domain is properly represented by a single service.
Also, I think this question belongs in https://softwareengineering.stackexchange.com.
With regard to the principles of OOP, I find exposing grouped services through public static
attributes even with final
keyword as a bad principle. I think this approach increases the complexity: What if more of the DAOs/services are about to be added? - Then you'll be forced to enlarge the MetaDao
and MetaSerice
classes.
Exposing these services through this facade, another unnecessary layer would allow the UserController
use and touch
services which really doesn't need - Is it necessary? It breaks the encapsulation. Use this widely used approach:
@RestController
public class UserController {
@Autowired
private final UserService userService;
// ...
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With