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