架構設計準則 筆記 - fantasy0107/notes GitHub Wiki
自己覺得的演化
依序往下細分將邏輯慢慢分開
- model + controller
- cache + repository + service + presenter(transformer)
- checker + constant + support + Exception code
- validator + (concrete)
資料層級
Controller 控制器
- 使用 Service 或 Concrete 的商業邏輯
- 做資料交易控制 (transaction)
- 使用 Checker 去檢查任何使用者傳進來的資料,確保資料的正確性
class PostController extends Controller
{
public function __construct(
PostConcrete $PostConcrete,
PostService $PostService,
CommentService $CommentService,
PostChecker $PostChecker
)
{
$this->PostConcrete = $PostConcrete;
$this->PostService = $PostService;
$this->CommentService = $CommentService;
$this->PostChecker = $PostChecker;
}
// 顯示文章
public function show($post_id) {
try {
// 驗證資料
$input = [
'post_id' => $post_id
];
$this->PostChecker->checkShow($input);
// 撈取文章
$Post = $this->PostConcrete->findPost($post_id);
// 撈取文章留言
$Comment = $this->CommentService->getCommentByPostId(post_id);
} catch (Exception $exception) {
throw $exception
}
}
// 更新文章
public function update($post_id) {
try {
// 驗證資料
$input = request()->all();
$input['post_id'] = $post_id;
$this->PostChecker->checkUpdate($input);
// 交易開始
DB::beginTransaction();
// 更新文章
$Post = $this->PostService->update($post_id, $input);
// 交易結束
DB::commit();
} catch (Exception $exception) {
// 交易失敗
DB::rollBack();
throw $exception
}
}
}
Service 服務
- 商業邏輯
- 資料驗證
- 組合不同的 Repository 資料
- 需要跨表格處理資料才使用
- 牽涉到外部行為 : 如發送Email,使用外部API
- 使用PHP寫的邏輯 : 如根據購買的件數,有不同的折扣
class PostService {
public function __construct(
PostRepository $PostRepository,
PostTagRepository $PostTagRepository
)
{
$this->PostRepository = $PostRepository;
$this->PostTagRepository = $PostTagRepository;
}
// 撈取文章
public function findPost($post_id) {
try {
// 撈取文章
$Post = $this->PostRepository->find($post_id);
// 撈取文章標籤
$Post->tag = $this->PostTagRepository->getByPostId($post_id);
return $Post;
} catch (Exception $exception) {
throw $exception
}
}
public function clearPostCache($post_id) {
try {
$Post = $this->findPost($post_id);
// 清除文章快取
$this->PostRepository->cache()->clearPostCache($Post);
// 清除文章標籤快取
$this->PostTagCache->clearPostTagCache($Post->tag);
} catch (Exception $exception) {
throw $exception
}
}
}
Repository 資料庫
- 撈取屬於自己 Model 不同條件下的資料
- 快取控制
- method 名稱中指出這個方法是要撈什麼樣的資料
- 呼叫單一表格資料邏輯應使用Repository (資源庫)
PostRepository->getWeekTopPosts(); // 取得本週熱門文章
class PostRepository {
public function __construct(
Post $Post,
PostCache $PostCache
)
{
$this->Post = $Post;
$this->PostCache = $PostCache;
}
public function cache() {
return $this->PostCache;
}
public function find($post_id) {
try {
$cache_key = $this->cache()->getPostCacheKey($post_id);
if (Cache::has($cache_key)) {
// 有快取資料
$Post = Cache::get($cache_key);
return $Post;
}
// 撈取資料庫文章資料
$Post = $this->Post->find($post_id);
if(!is_null($Post)) {
// 紀錄快取
$this->cache()->putPost($Post);
}
return $Post;
} catch (Exception $exception) {
throw $exception
}
}
public function findLatestPost() {
try {
$cache_key = $this->cache()->getLatestPostCacheKey($post_id);
if (Cache::has($cache_key)) {
// 有快取資料
$$Posts = Cache::get($cache_key);
return $Post;
}
// 撈取資料庫文章資料
$Post = $this->Post
->order('created_at', 'desc')
->first();
if(!is_null($Post)) {
// 紀錄快取
$this->cache()->putLatestPost($Post);
}
return $Post;
} catch (Exception $exception) {
throw $exception
}
}
}
Model 模型
- 資料表存取相關設定
- 越乾淨越好
- Property : 如$table,$fillable…等
- Mutator: 包括 mutator 與 accessor。
- Method : relation 類的 method,如使用 hasMany() 與 belongsTo()。
class Post extends Model
{
protected $table = 'post';
protected $fillable = [];
protected $primaryKey = 'id';
protected $dates = ['created_at', 'updated_at'];
protected $presenter = 'PostPresenter';
}
Presenter 資料呈現
- 協助 Model 做資料呈現處理
- 有點像 laravel model cast property
- 這一層可以做成 transformer 變成轉換回傳格式
class PostPresenter extends Presenter
{
public function created_at_human_time()
{
return $this->created_at->diffForHumans();
}
}
Cache 快取
- 協助 Repository 做快取資料的控制
class PostCache {
public function getPostCacheKey($post_id) {
// 撈取文章快取鍵值
}
public function putPost(Post $Post) {
// 紀錄文章快取
}
public function clearPostCache(Post $Post) {
// 清除文章快取
}
}
Checker 檢查器
- 協助 Controller 做資料驗證
- Checker (檢查器) 只檢查多表格資料內容
class PostValidator {
public function checkFindPost($input){
// 驗證文章資料
$this->PostValidator->validatePostId($input);
$this->PostValidator->validatePostContent($input);
// 驗證會員資料
$this->MemberValidator->validateMemberId($input);
}
}
Validator 驗證器
- 協助 Checker 做資料驗證
- Validator (驗證)只檢查單一表格資料內容
class PostValidator {
public function validatePostId($input){
// 設定驗證規則
$rules = [
'post_id' => [
'required',
'max:20',
],
];
// 開始驗證
$this->validator = Validator::make($input, $rules);
if ($this->validator->fails()) {
throw new Exception(
'文章編號格式錯誤',
PostExceptionCode::POST_ID_FORMAT_ERROR
);
}
}
}
Concrete 服務組合
- 協助 Controller 組合不同 Service 的資料成商業邏輯
class PostConcrete {
public function __construct(
PostService $PostService,
UserService $UserService
)
{
$this->PostService = $PostService;
$this->UserService = $UserService;
}
// 撈取文章資料
public function findPost($post_id){
try {
// 撈取文章
$Post = $this->PostService->findPost($post_id);
// 撈取文章作者資料
$user_id = $Post->user_id;
$Post->user = $this->UserService->findUser($user_id);
return $Post;
} catch (Exception $exception) {
throw $exception
}
}
}
Constant 常數
- 共用變數名稱設定
- 皆為靜態變數
class PostConstant {
const POST_TYPE_PUBLIC = 'P';
const POST_TYPE_DELETE = 'D';
}
Support 支援
- 共用方法
- 皆為靜態方法
class PostSupport {
// 撈取所有文章類型
public static function getAllPostType() {
$all_post_type = [
PostConstant::POST_TYPE_PUBLIC,
PostConstant::POST_TYPE_DELETE,
];
return $all_post_type;
}
}
ExceptionCode 例外代碼
- 共用例外代碼設定
- 錯誤代碼 (error_code) 會在不同的地方被存取 為了管理及閱讀性方便 分出這一層
- 皆為靜態變數
class PostExceptionCode {
const POST_ID_FORMAT_ERROR = 10000001;
const POST_NOT_FOUND = 10000002;
const POST_TAG_NOT_FOUND = 10000003;
}
注意
不能跨 2 階層以上存取
Controller 不能存取 Repository
低階層的不能存取高階層的資料
Model 不能存取 Repository
同一個資料類型,不能互相呼叫
Concrete 不能呼叫 Concrete