前端和後端目錄說明
1. 資料夾和資料夾內部檔案的語義一致性
模組資料夾應該保證單一入口和出口,怎麼理解呢。
project
|-- src
|-- components
|-- input
|-- index.js
|-- index.module.scss
|-- pages
|-- seller
|-- components
|-- input
|-- index.js
|-- index.module.scss
|-- reducer.js
|-- saga.js
|-- index.js
|-- index.module.scss
|-- buyer
|-- index.js
|-- index.js
一般來說我們的目錄結構都會有一個資料夾是按照路由模組來劃分的,這個資料夾一般叫 pages 或者是 routes。這個資料夾裡面應該包含我們專案所有的路由模組(上面的例子路由模組指的是 seller 和 buyer),並且僅應該包含路由模組,而不應該有別的其他的非路由模組的資料夾。
這樣做的好處在於一眼從 pages 資料夾看出這個專案的路由,比如上面的例子我們就可以說有/seller
和/buyer
兩個路由,如果混入了其他非路由的資料夾就會混淆這裡面都是路由資料夾的約定。
2. 單一入口/出口
接著上面的例子來講,seller 資料夾應該作為一個獨立的模組由外部引入,並且seller/index.js
seller/index.js
檔案引入,而不應該直接從seller/reducer.js
引入。因為外部不應該知曉路由資料夾內部的檔案分佈,而是應該全部從index.js
匯入。同時 seller 內部檔案不管如何分佈,需要外部引入的都要在入口檔案index.js
匯出。
// rootReducer.js
// 錯誤用法
import sellerReducer from 'src/pages/seller/reducer'
// 正確用法
import { reducer as sellerReducer } from 'src/pages/seller'
// seller/index.js
// 同時
import reducer from './reducer'
export {
reducer
}
這樣做的好處在於,無論你的模組資料夾內部有多亂,外部引用的時候,都是從一個入口檔案引入,這樣就很好的實現了隔離,如果後續有重構需求,你就會發現這種方式的優點
3. 就近原則,緊耦合的檔案應該放到一起,且應以相對路徑引用
繼續使用上面的例子,在seller/index.js
中使用樣式,一般我們會用相對路徑的方式。這裡為什麼不用絕對路徑,可能有人說是寫起來方便,但是實際上這裡用相對路徑可以保證這個 seller 模組內部的獨立性。
// seller/index.js
// 正確用法
import styles from './index.module.scss'
// 錯誤用法
import styles from 'src/pages/seller/index.module.scss'
怎樣理解?我們現在的 seller 目錄是在src/pages/seller
,如果我們後續發生了路由變更,需要加一個層級,變成src/pages/user/seller
。如果我們採用第一種相對路徑的方式,那就可以直接將整個資料夾拖過去就好,seller 資料夾內部不需要做任何變更。但是如果我們採用第二種絕對路徑的方式,移動資料夾的同時,還需要對每個 import 的路徑做修改。
這樣做的好處?就是上面說的。
4. 公共的檔案應該以絕對路徑的方式從根目錄引用
公共指的是多個路由模組共用,上面的src/components/input
就是一個公共元件 。如果我們要在 buyer 模組使用這個元件可能有下面兩種引用方式
// buyer/index.js
// 錯誤用法
import Input from '../../components/input'
// 正確用法
import Input from 'src/components/input'
為什麼?和上面的原因一樣,如果我們需要對資料夾結構進行調整。將/src/components/input
變成/src/components/new/input
,如果使用絕對路徑,只需要全域性搜尋替換。而如果使用相對路徑,則需要一個個找,很麻煩。
其實方便資料夾結構調整是次要的,主要的原因還是絕對路徑有全域性的語義,相對路徑有獨立模組的語義。這點會方便新上手的人熟悉這個模組是共用的還是私有的。
5. /src 外的檔案不應該被引入
這一點就比較好理解了,而且其實已經有腳手架做了相關的約束了,正常我們的前端專案都會有個 src 資料夾,裡面放著所有的專案需要的資源,js, css, png, svg 等等。src 外會放一些專案配置,依賴,環境等檔案。所以,src 資料夾外不應該放需要被引入的資源。
這樣的好處是方便劃分專案程式碼檔案和配置檔案
總結
專案的目錄結構很重要,因為目錄結構能體現很多東西,怎麼規劃目錄結構可能每個人有自己的理解。但是上面這些原則是我個人覺得是比較通用的,如果一個專案能遵循上面的原則,至少可以說,不會亂。
這裡有兩個點我覺得是需要拎出來再強調一遍的:
- 一個是約定帶來的語義,上面的 5 點都體現了這點,約定帶來的語義可以很大程度減少我們理解專案的時間和難度
- 另一個就是面向重構程式設計,其實無論是使用 TypeScript 還是像上面的 3,4點一樣約定對模組檔案和公用檔案的引用方式,都是為了在專案結構調整或者重構的時候,更快更安全的對檔案進行修改。一個成熟的長期維護的專案,是一定要把重構的坑埋到程式碼裡面的。
後端伺服器的組成: pom.xml(Maven專案配置檔案) + java資料夾 + resource資料夾
- 程式碼層(java),根目錄com.xxx:XxxApplication.java + 對應模組程式碼(domain + controller + service + mapper等)
- XxxApplication.java(專案主入口,main方法)
- controller: 控制層,請求介面
- service: 服務層,邏輯程式碼,資料服務的實現介面(serviceImpl)UserService.java 和 UserServiceImpl.java
- mapper: 資料層,或者dao, 比如UserMapper.java 、UserMapper.xml
-
domain: 實體類,同 bean、entity、model
bean:任何一個java類都可以成為一個bean,這個類裡包含物件的屬性、get、set方法及其他的業務邏輯。
model:model是MVC中的概念,可以理解為View層展示資料的物件。
entity:資料表對應到實體類的對映。
- 資源層(resource):存放資原始檔,比如郵件html、mapper
- email郵件模板,比如registerSuccess.html
- properties配置檔案,比如mybatis.properties
- mapper檔案,比如UserMapper.xml(也可以寫到程式碼層的mapper資料夾中)
- template模板
- application.yml
- log4j2.xml日誌配置
// 根目錄結構 -src: -main: -java: - com.xxx -resource: - -test: -target: -pom.xml
- src/main/java: 程式碼檔案目錄
- src/main/resource: 資原始檔目錄
- pom.xm:Maven專案配置檔案
// java程式碼檔案目錄: 檔案目錄按如下進行規範命名 -java: -com.xxx: -entity -controller -service -mapper -util -XxxApplication.java
- entity:實體類,也可以命名為bean、entity、model,例如User.java
- controller: 控制層,請求介面,例如UserController.java
- service: 服務層,以及關聯的介面檔案,例如UserService.java(impl/UserServiceImpl.java)
- mapper: 資料層,也可以命名為dao,例如UserMapper.java和UserMapper.xml
- model: 請求使用到的實體類: xxxRequestTO.java、xxxReponseTO.java
- config、constant、util等配置檔案
- XxxApplication.java : 專案主入口,main方法
// resource資原始檔目錄 -resource: -mapper -static -template -application.yml
- application.yml: 配置檔案,也可命名為application.properties
// 資料庫配置 -- application.yml spring: datosource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/dataBaseName?userUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8 username: youMysqlUsername password: yourMysqlPassword