Spring MVC 项目结构
Posted
技术标签:
【中文标题】Spring MVC 项目结构【英文标题】:Spring MVC project structure 【发布时间】:2021-02-19 20:34:56 【问题描述】:我正在决定一个小型 RESTful 服务的项目结构。至于现在,我看到了two designs。首先是功能。很多tutorials、another tutorial也采用这种方式:
example
+- controller
| +- CustomerController
| +- OrderController
+- model
| +- Customer
| +- Order
+- repository
| +- ...
+- security
+- service
Spring 文档推荐另一种方法,按对象分组。但是,我并不经常看到它:
example
+- customer
| +- Customer
| +- CustomerController
| +- CustomerRepository
| +- CustomerService
+- order
| +- ...
您能否详细说明何时选择其中之一?还是仅仅是一种偏好?特别是对于 RESTful 和 RESTless 应用程序。另外,如果我按对象对包进行分组,我应该将未公开的安全相关类放在哪里?
另外,根据answers之一的说法,按功能分组会在以后引起问题。有评论,但自 2014 年以来没有得到答复。
其次,对于实现类,有一个建议,也在这个page 上,避免使用Impl
后缀。为什么会这样? Spring 是否没有明确要求 Impl
并将其用作 Spring Data JPA 存储库的默认值?
CustomerRepository implements JpaRepository, ComplexCustomerRepository ...
ComplexCustomerRepositoryImpl implements ComplexCustomerRepository
// Specifications and Examples and custom queries here
如果这是一种反模式,那么解决方法是什么?我是不是误会了什么?
谢谢。
【问题讨论】:
【参考方案1】:有 1000+1 个解决方案,有很多人或公司有实施。没有关于它应该是什么样子的文献。这取决于设计、想法和项目计划。
我的 Rest 基础结构没有模板。 Ofc,对于 API,MVC 不是很有效。因为 View 本身就是响应模型。
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)
补充
如果你使用@Service注解,不需要接口。我就是这样使用它的:
@Service
public class AnyService ...
使用接口只是因为看到任何地方但从不引用,这是不必要的,也不是必需的。如果您有 2 个不同的模型使用相同的方法(例如 getUuid()
),并且您想将两者都用于一个参数,那么您需要给它一个接口。
基本上有2+1的名字识别方法。 它们只是名称。他们没有定义任何东西!只有当您使用文件管理器查看它时,您才会看到它是什么。 IDE 显然知道并在文件夹树中显示。
接口以大 I 开头,但实现有一个正常的名称。或者另一个,接口有一个普通的名字,但实现最后有 Impl。也有不区分的情况(不加“I”或“Impl”),但IDE显示接口或类,见IntelliJ或Eclipse。
【讨论】:
【参考方案2】:请注意,我从未使用过 Spring。
一些原则:
MVC separates the concerns,例如presentation logic (V & C) 来自 business logic (M)。 基于 MVC 的应用程序的核心是 域模型。不是数据库,不是某些功能或模块结构。 DM 对为其开发应用程序的业务进行抽象、建模。因此,DM 组件之间的交互实现了业务逻辑。 多个应用程序应该可以同时访问一个 DM,无论它们有多么不同。换句话说,DM 组件应该是独立于应用程序的。例如。特定应用程序的任何代码都不应驻留在 DM 中。 只能通过特定于应用程序的应用程序服务访问 DM(作为应用程序的一部分并在 DM 之外定义)。所以,不是直接由控制器。不过,您也可以拥有独立于应用程序的域服务(作为 DM 的一部分),可由应用程序服务调用。 DM 应该(大部分)由抽象组成(接口或抽象类)作为组件。它们的实现应该驻留在 DM 之外的某个地方(例如,在 “Infrastructure” 文件夹/命名空间中)。 repositories 是 DM 的一部分。更准确地说,如上所述,它们的抽象。另一方面,data mappers(也称为 DAO,例如数据访问对象)是基础架构的一部分。这样,存储库将域模型与数据访问或持久层分离。模型对此一无所知。结构示例:
考虑到上述情况,想象一下您自己的结构类似于下面的结构(基于 php;可能过于详细),描述了域模型与任何应用程序的解耦。然后您会看到,您提出的第二种方法(“按对象分组”)有什么不完全正确的地方。例如,如果您想在多个模块中使用同一个实体(此处为:Customer
),您会怎么做?
您的第一种方法也不太正确,因为域模型不仅仅由实体组成。除非您将文件夹 model
重命名为 entity
并将文件夹 entity
和 repository
视为 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")]
+ ...
不过,考虑到您只开发一个基于 MVC 的应用程序,您可以将“domain”文件夹放入“path/to/myapp/src”,相应地更改命名空间(Java 中的包)。
+ 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
【讨论】:
以上是关于Spring MVC 项目结构的主要内容,如果未能解决你的问题,请参考以下文章
使用 Maven 的具有多个子项目的 Spring MVC 项目的包结构