-1

I am deciding the project structure for a small RESTful service. As for now, I see two designs. The first is by functionality. Many tutorials, another tutorial also uses this approach:

example
+- controller
|  +- CustomerController
|  +- OrderController
+- model
|  +- Customer
|  +- Order
+- repository
|  +- ...
+- security
+- service

The other approach, grouping by objects, is recommended by Spring docs. However, I am not seeing it quite often:

example
+- customer
|  +- Customer
|  +- CustomerController
|  +- CustomerRepository
|  +- CustomerService
+- order
|  +- ...

Can you please elaborate on when to choose one over the other? Or is it merely a preference? Especially for RESTful and RESTless applications. Also, if I group packages by objects, where should I place security-related classes that are not exposed?

In addition, according to one of the answers, grouping by funtionality will casuse problems later. There is a comment, but has not been answered since 2014.

Secondly, for implementation classes, there is a suggestion, also on this page, to avoid Impl suffixes. Why is it the case? Does Spring not explicitly require Impl and uses it as a default for Spring Data JPA Repositories?

CustomerRepository implements JpaRepository, ComplexCustomerRepository {...}

ComplexCustomerRepositoryImpl implements ComplexCustomerRepository{
  // Specifications and Examples and custom queries here
}

If this is an anti-pattern, what is the work around? Am I misunderstanding something?

Thank you.

Biliking
  • 136
  • 1
  • 10

2 Answers2

1

There are 1000+1 solutions, many people or companies as there are implementations. There is no literature on what it should look like. It depends on design, idea and project plan.

My structure for Rest base without template. Ofc, for APIs, MVC is not very valid. Because View itself is the response model.

src
+- http (Things about http)
|  +- controller (where catch request, additional folders can be categories)
|  +- model (request and response models)
|  +- errors (tipical Exceptions for HTTP errors what handle by @RestControllerAdvice)
|  +- validators (extended annotations - https://www.baeldung.com/spring-mvc-custom-validator)
+- database
|  +- cassandra
|  |  +- repositories
|  |  +- models
|  +- redis
|  |  +- ...
|  ...
+- models (general model in program what I can pass to another component easily)
+- services
|  +- utils
|  +- authentication
|  ... (and so on)
+- email
+- helper
+- configs (tipical Spring Configuration & environment properties)
+- consts (example enums or static classes)
... (and more what you want)

Supplement

If you use @Service annotation, not need interface. That's how I use it:

@Service
public class AnyService { ... }

Using an interface only because seeing anywhere but never references, it is unnecessary and not required. If you have 2 different models what use same (example getUuid()) method and you want to use both into one parameter then you need to give it an interface.

There are basically 2+1 name identifications methods. They are just names. They don't define anything! Only if you look at it with a file manager will you see what it is. IDE obviously knows and show you in folder tree.

The interface starts with a large I, but the implementation has a normal name. Or the other, where the interface has a normal name but the implementation has Impl at the end. There are also cases where it is not differentiated (no added "I" or "Impl"), but the IDE shows that the interface or class, see IntelliJ or Eclipse.

Numichi
  • 926
  • 6
  • 14
1

Note that I've never worked with Spring.

Some principles:

  • MVC separates the concerns, e.g. the presentation logic (V & C) from the business logic (M).
  • The core of an MVC based application is the domain model. Not the database, not some functionality or modules structure. The DM abstracts, models the business for which the application(s) is/are developed. So, the interaction between the DM components implements the business logic.
  • A DM should be accessible by multiple applications at the same time, no matter how different they'd be. In other words, the DM components should be application-independent. E.g. no code specific to a certain application should reside in the DM.
  • A DM should be accessed through application-specific application services only (as part of the application and defined outside of the DM). So, not directly by controllers. Still, you could also have application-independent domain-services (as part of the DM), callable by the application services.
  • A DM should consist (mostly) of abstractions (interfaces or abstract classes) as components. Their implementation should reside somewhere outside of DM (for example, in a "Infrastructure" folder/namespace).
  • The repositories are part of the DM. More precise, as said above, their abstractions. On the other hand, the data mappers (also known as DAO's, e.g. Data Access Objects) are part of the infrastructure. That way, the repositories decouple the domain model from the data access or persistence layer. The model knows nothing about it.

Example of structure:

Taking the above into consideration, imagine yourself a structure like the one bellow (PHP-based; maybe too detailed), depicting the decoupling of the domain model from any application. You'll see then, what is not quite right with the second approach that you presented ("grouping by objects"). For example, what will you do if you'll want to use the same entity (here: Customer) in multiple modules?

Your first approach is not too correct, too, because the domain model is not composed just of entities. Unless you'll rename the folder model to entity and consider the folders entity and repository as part of the DM.

+ path/to/domain
    + src
        + Infrastructure [namespace: "Domain\Infrastructure"]
            + Mapper [data mappers; as defined by Martin Fowler at https://www.martinfowler.com/eaaCatalog/dataMapper.html]
                + Customer
                    + PdoCustomerMapper.php [implements "CustomerMapper"; access to db through PDO]
                    + CustomerMapper.php [interface; used by "Domain\Model\Customer\CustomerCollection"]
            + Repository [repositories]
                + Customer
                    + CustomerCollection.php [repository; implementation of "Domain\Model\Customer\CustomerCollection" interface]
            + Service [various services; implementations of the interfaces defined in "Domain\Model" (email, security, payment, etc)]
                + Email
                    + SMTPSender.php [email sender; implementation of "Domain\Model\Email\EmailSender"; sends emails through SMTP]
                + Security
                    + AbcAuthentication.php [authentication; an implementation of "Domain\Model\Security\Authentication"]
                    + DefAuthentication.php [authentication; another implementation of "Domain\Model\Security\Authentication"]
                    + XyzAuthorization.php [authorization; an implementation of "Domain\Model\Security\Authorization"]
        + Model [this is DM (the domain model, the core of your application(s)); namespace: "Domain\Model"]
            + Customer
                + Customer.php [entity; implementation]
                + Name.php [value object; implementation; "<first name>, <last name>"]
                + CustomerCollection.php [interface; repository (envisioned as a Customer collection); uses "Domain\Infrastructure\Mapper\Customer\CustomerMapper"; as defined by Martin Fowler at https://www.martinfowler.com/eaaCatalog/repository.html]
                + CustomerService.php [implementation; domain service, application-independent]
            + Email
                + EmailSender.php [interface; domain service, application-independent; its implementation must not reside in "Infrastructure" folder (it can, for ex., be an external library loaded by a dependency manager in a "path/to/myapp_1/vendor" directory)]
            + Security
                + Authentication [interface; domain service, application-independent; its implementation must not reside... dito]
                + Authorization [interface; domain service, application-independent; its implementation must not reside... dito]


+ path/to/myapp_1
    + config
        + dependencies [the dependency injection container definitions]
        + parameters [arguments for the DI container definitions]
        + routes [route definitions]
    + public
        + assets
            + css
                + ... [files resulting from the compilation of the .scss files]
            + js
            + images
        + index.php [the entry point of the application. All user requests are handled by this file]
    + resources
        + fonts [the font files loaded by the @font-face css rules]
        + scss (with Dart Sass as Sass engine)
        + templates [with Twig as templating engine]
            + layouts
                + Primary.html.twig
            + templates [their content is embedded into "Primary.html.twig" layout]
                + Dashboard.html.twig
                + Customers
                    + AddCustomer.html.twig
                    + ListCustomers.html.twig
        + vendor
            + node_modules [all client-side libraries (Bootstrap, jQuery, etc), loaded through a package manager, like npm]
            + package.json
    + src
        + Controller
        + View
        + Service [application services, application-specific; they use the components of "Domain\Model" to read/update the DM]
            + Customer
                + AddCustomer [adds a customer]
                    + Exception
                        + CustomerExists.php
                    + AddCustomer.php [uses "Domain\Model\Customer\CustomerCollection"]
                + ListCustomers.php [list specified customers; uses "Domain\Model\Customer\CustomerCollection"]
            + Order
                + ...
    + storage
        + cache
            + router
                + routes.cache [compiled routes]
            + container [compiled DI container]
        + sessions [opened sessions]
    + tests
    + vendor [all server-side libraries (FastRoute, PSR-7, etc), loaded through a dependency manager, like Composer. Including an eventual used framework (like Slim Framework)]
    + composer.json
    + .env [default values for the environment variables corresponding to a system in production. The values can be overwritten by the ones found in other .env.* files, like ".env.local". This file must be git commited to the production server]
    + .env.local [values for the environment variables, which overwrite the default ones in the file ".env". This file should be created locally, by the developer, on her/his machine, and must not be git commited to the production server]


+ path/to/myapp_2 [this application can look a lot different from myapp_1. Though, it can have access to the same domain model ("Domain\Model")]
    + ...

Though, considering that you're developing only one MVC-based application, you can place the "domain" folder into "path/to/myapp/src", changing the namespaces (packages in Java) correspondingly.

+ path/to/myapp
    + config
    + public
    + resources
    + src
        + Controller
        + View
        + Service [application services, application-specific]
            + Customer
                + AddCustomer
                    + Exception
                        + CustomerExists.php
                    + AddCustomer.php
                + ListCustomers.php
            + Order
                + ...
        + Domain
            + Infrastructure
                + Mapper
                    + Customer
                        + PdoCustomerMapper.php
                        + CustomerMapper.php
                + Repository [repositories]
                    + Customer
                        + CustomerCollection.php
                + Service
                    + Email
                        + SMTPSender.php
                    + Security
                        + AbcAuthentication.php
                        + DefAuthentication.php
                        + XyzAuthorization.php
            + Model [the DM]
                + Customer
                    + Customer.php
                    + Name.php
                    + CustomerCollection.php
                    + CustomerService.php [domain service, application-independent]
                + Email
                    + EmailSender.php [domain service, application-independent]
                + Security
                    + Authentication [domain service, application-independent]
                    + Authorization [domain service, application-independent]
    + storage
    + tests
    + vendor
    + composer.json
    + .env
    + .env.local
PajuranCodes
  • 303
  • 3
  • 12
  • 43