在我繼續我的後端開發之旅時,一個問題一直困擾著我:為什麼我們不能把所有的邏輯都寫在控制器裡?感覺很簡單,抓取資料、應用一些規則、更新資料庫,所有這些都在一個地方完成。哦,我的確這麼做了,但我知道這對大公司來說效率不高,而且你不可能用這樣的程式碼打入大公司的市場,但我從未真正理解它,也不知道為什麼。
但現實是:雖然這對小型業餘專案來說可能沒問題,但在大型應用程式(例如金融系統)中,它很快就會變得混亂。這就是為什麼大公司堅持使用分層架構的原因。將職責劃分為控制器、業務邏輯(服務)和儲存庫。
很長一段時間裡,我無法完全理解這種分離的重要性。我研究、實驗,也曾苦苦掙扎,直到有一天我終於明白了。一旦我理解了,它就徹底改變了我對軟體架構的思考方式。我知道很多人也走著同樣的路,所以我想用簡單易懂的語言來分享這些知識,讓大家最終理解。
讓我們透過簡單的類比、清晰的圖表和實用的程式碼來深入研究它。
| 層 | 它是誰 | 它做什麼 |
| ---------------- | ------------------- | -------------------------------------------------------------- |
|控制員| 前台接待員 | 接收請求並傳遞 |
|服務| 銀行經理 | 應用規則、做決策、執行政策 |
|儲存庫| 記錄員 | 取得並更新系統中的記錄 |
| ILogger | 安全攝影機 | 保存發生的事情的歷史記錄(稽核/日誌)|
| Serilog/NLog | 儲藏室 | 決定日誌的儲存位置|
[ User Request ]
↓
[ Controller ] → Receives request and passes it along to the Services
↓
[ Service ] → Applies business rules and logic
↓
[ Repository ] → Talks to the database (fetching and updating details only)
控制器是入口點。它不做決策,也不與資料庫互動;它只是接收請求並轉發。
[ApiController]
[Route("api/transfer")]
public class TransferController : ControllerBase
{
private readonly ITransferService \\\_service;
public TransferController(ITransferService service)
{
_service = service;
}
[HttpPost]
public IActionResult TransferMoney([FromBody] TransferRequest request)
{
_service.Transfer(request.FromAccount, request.ToAccount, request.Amount);
return Ok("Transfer completed");
}
}
服務(業務邏輯層)應用規則。例如,“發送者是否有足夠的錢進行轉帳?”
public class TransferService : ITransferService
{
\ private readonly IAccountRepository \\\_repository;
\ private readonly ILogger<TransferService> \\\_logger;
\ public TransferService(IAccountRepository repository, ILogger<TransferService> logger)
\ {
\ \\\_repository = repository;
\ \\\_logger = logger;
\ }
\ public void Transfer(string fromAcc, string toAcc, decimal amount)
\ {
\ var from = \\\_repository.GetAccount(fromAcc);
\ var to = \\\_repository.GetAccount(toAcc);
\ if (from.Balance < amount)
\ {
\ \\\_logger.LogWarning("Insufficient funds for account {Account}", fromAcc);
\ throw new Exception("Insufficient funds");
\ }
\ from.Balance -= amount;
\ to.Balance += amount;
\ \\\_repository.UpdateAccount(from);
\ \\\_repository.UpdateAccount(to);
\ \\\_logger.LogInformation("Transferred {Amount} from {From} to {To}", amount, fromAcc, toAcc);
\ }
}
儲存庫是資料存取發生的地方。沒有規則,沒有決策——只是讀取和寫入資料。
public class AccountRepository : IAccountRepository
{
\ private readonly BankingDbContext \\\_context;
\ private readonly ILogger<AccountRepository> \\\_logger;
\ public AccountRepository(BankingDbContext context, ILogger<AccountRepository> logger)
\ {
\ \\\_context = context;
\ \\\_logger = logger;
\ }
\ public Account GetAccount(string accountId)
\ {
\ \\\_logger.LogInformation("Fetching account {AccountId}", accountId);
\ return \\\_context.Accounts.FirstOrDefault(a => a.Id == accountId);
\ }
\ public void UpdateAccount(Account account)
\ {
\ \\\_context.Accounts.Update(account);
\ \\\_context.SaveChanges();
\ \\\_logger.LogInformation("Updated account {AccountId}", account.Id);
\ }
}
在金融領域,日誌記錄至關重要。每筆交易、每條成功或每條錯誤都必須追蹤。這正是ILogger 的用武之地。
\\\_logger.LogInformation("Transaction {TransactionId} completed at {Time}", transaction.Id, DateTime.UtcNow);
\\\_logger.LogError("Failed to process transaction {TransactionId}", transaction.Id);
但是 ILogger 只是一個介面。你仍然需要像Serilog或NLog這樣的日誌提供者來決定將這些日誌儲存在何處(檔案、雲端、資料庫等)。
// Program.cs setup example with Serilog
Log.Logger = new LoggerConfiguration()
\ .WriteTo.File("logs/transactions.txt")
\ .CreateLogger();
builder.Host.UseSerilog();
| 層 | 職責 | 類比 |
| ------------ | -------------------------------------- | ---------------- |
| 控制器 | 接受請求並回傳回應 | 前台接待員 |
| 服務 | 應用業務規則 | 銀行經理 |
| 儲存庫 | 處理資料存取 | 記錄員 |
| ILogger | 記錄操作、錯誤與審核 | 安全攝影機 |
| Serilog/NLog | 儲存日誌(檔案、資料庫、雲端等)| 儲藏室 |
小型專案:你可以把控制器裡的所有東西混合起來。這樣可以工作一段時間。
大公司:這種架構是強制性的。它開發的應用程式:
更容易測試
更容易擴展
更易於維護
更安全(特別是與金錢相關的系統)
請這樣查看:
如果是關於處理 API 請求 →控制器
如果是關於規則、邏輯或決策 →服務
如果是關於取得或儲存資料 →儲存庫
如果是關於追蹤活動→ ILogger (使用 Serilog/NLog 來儲存它)
如果你正在開發一個業餘專案,你可以偷工減料。但如果你的目標是在大公司工作,或是開發能夠持續發展和發展的軟體,那麼這種分層架構就是你必須遵循的基礎。
時時思考:前台職員、銀行經理、記錄員和保全攝像頭,每個人都有各自的職責。這有助於您記住並了解程式碼應該屬於哪個分類/部分。將它們分開管理,可以讓您的系統保持整潔、可擴展且專業。
圖形來源:Ramesh Fadatare
LinkedIn: LinkedIn
推特: Twitter
Instagram: Instagram