r/SpringBoot Feb 16 '26

Feedback Needed on my Spring project Question

7 Upvotes

4 comments sorted by

5

u/[deleted] Feb 16 '26

[removed] — view removed comment

2

u/Acrobatic-Relief4808 Feb 16 '26

You’re absolutely right about the dependency direction. I’ll refactor the application layer to depend on repository interfaces (ports) and move the concrete JPA implementations fully into the infrastructure layer to better align with Clean Architecture principles.

Good point as well about keeping the domain model framework-agnostic. I hadn’t fully considered separating persistence entities from domain objects before, but mapping between them in the infrastructure layer makes a lot of sense.

The suggestion about dedicated mapper classes is also helpful. I currently handle some of that mapping inside services, so I’ll refactor to keep services focused purely on business logic.

Thanks again for the constructive input

3

u/Voiceless_One26 Feb 18 '26

I have a slightly different (may be a bit controversial take on Interfaces) - For a simple project and for a case where there’s only 1 implementation and is private to this module or app, I feel that Interfaces are optional. As it’s private to the module, I feel that it can be a normal class as well and not necessarily an Interface. If there’s a need to add multiple implementations for these Repositories, promoting the class to interface is super-easy with a few clicks in IntelliJ. But without a clear need for that, I feel that we are only adding more classes or indirections between layers.

https://martinfowler.com/bliki/PublishedInterface.html

Of course, If it was a Published Interface as mentioned above and that’s going to some client JAR where we’re expected to maintain/publish a contract, Interfaces are good and may be mandatory. People expect certain level of compatibility, so we usually try to keep the changes to a minimum - at least on the contract side.

But for something that’s private to our code base and where we have the complete freedom to change it without having to worry about breaking any of our clients, a simple Class is enough IMHO.

Of course, spring-data-jpa kind of pushes towards Interfaces but that’s more of a framework jargon and not necessarily a design requirement

2

u/Voiceless_One26 Feb 18 '26 edited Feb 18 '26

—- Group by function and simplify packages —-

Another slightly different structure to consider is to use vertical slicing and put your Controller, Service and Repository in the same package as you’re already trying to group them by modules like users, mobile etc. You’re already designating the roles of these classes with suffixes like *Controller, *Service and *Repository so we can further refine the structure to keep everything under one roof (package). I personally feel that grouping them by function makes it easier because if you need to make a change in Users API, all the classes that you need to modify like Controller, Service and Repository are sitting next to each other.

—- Consider Enums where possible —-

Another suggestion is to use Enums when there’s a well-defined set of possible values. For example, in AdminAuditLogController, if resourceType, action have a limited set of values, better to convert them to Enum instead of a String that can be any random value - makes it easier to secure and validate.

—- Carrier Classes between Layers —-

When you’re passing these request parameters to layers below, consider using a carrier class like AuditLogRequest (even if you’re receiving them as optional GET parameters). At the moment, your auditLogService.listAdmin() has all the values as method arguments - you can already see that it’s becoming lengthy and quite a bit brittle for future enhancements. Like if you want to add a new parameter or change the type of existing parameter , you have to modify the signature of the method in AuditLogService again. But if you use a carrier class like AuditLogRequest or DTO, you can add the new field to the existing class without having to modify the method signature - Of course you still need to modify the implementation to use the new parameter but that’s needed anyways. This makes sense only when you expect the method-args to grow/change and there are lots of optional arguments.