5.1 — Clean & Hexagonal Architecture: một ý tưởng, nhiều tên gọi

1. Gỡ rối tên gọi trước — vì chúng là MỘT ý tưởng Hexagonal/Ports & Adapters (Cockburn 2005), Onion (Palermo 2008), Clean Architecture (Martin 2012) — ba “trường phái” mà thực chất là ba bản vẽ của cùng một nguyên tắc, nguyên tắc mà bạn đã học trọn ở chương 2.5: Nghiệp vụ ở trung tâm, không biết gì về thế giới ngoài. Mọi mũi tên phụ thuộc trỏ VÀO trong. Thế giới ngoài (DB, HTTP, queue, UI) nói chuyện với trung tâm qua các cổng (interface) do TRUNG TÂM định nghĩa. ...

July 17, 2026 · 5 min

5.0 — Domain-Driven Design: thiết kế theo nghiệp vụ, không theo công nghệ

1. Problem Statement — căn bệnh mà DDD sinh ra để chữa Level 1–4 làm việc ở quy mô hàm, struct, package. Level 5 hỏi câu hỏi quy mô hệ thống — và bắt đầu bằng căn bệnh phổ biến nhất của hệ thống lớn, một căn bệnh không phải về code: Team kỹ thuật và team nghiệp vụ nói hai ngôn ngữ khác nhau về cùng một thứ. Triệu chứng cụ thể, quen thuộc đến đau lòng: PM nói “khách hàng”, codebase có User, Account, Customer, Client, Member — năm tên cho không-rõ-mấy khái niệm; bảng orders có cột status 14 giá trị trong đó 5 giá trị “không còn dùng nhưng không ai dám xóa”; tính năng mới cần ba cuộc họp để thống nhất “ý anh nói đơn hủy là hủy kiểu nào?”; và bug nghiệp vụ sinh ra không phải vì code sai thuật toán, mà vì dev hiểu sai luật chơi của domain. ...

July 17, 2026 · 8 min

4.13 — Pipeline, Plugin, Domain Events, Saga & Outbox — và bản đồ tổng kết Level 4

Chương khép Level 4: hai idiom kiến trúc nhỏ (Pipeline, Plugin — phần lớn đã xây ở các chương trước, giờ chốt sổ), rồi bộ ba pattern của thế giới phân tán (Domain Events → Saga → Outbox — ba nấc của cùng một câu chuyện), và bảng phân loại cuối cùng: đâu là GoF, đâu là Enterprise, đâu là idiom Go. A. Pipeline — chốt sổ Đã xây ở 3.4 (Replace Loop with Strategy → tách khung khỏi rule) và 4.5 (middleware = pipeline của handler). Định nghĩa chốt: chuỗi giai đoạn xử lý, output giai đoạn này là input giai đoạn sau, mỗi giai đoạn không biết gì về nhau ngoài hợp đồng dữ liệu. Ba hình dạng trong Go, chọn theo tải: ...

July 17, 2026 · 8 min

4.12 — Repository, Unit of Work & Specification: bộ ba Enterprise Pattern quanh dữ liệu

Ba pattern từ danh mục Enterprise (Fowler, Patterns of Enterprise Application Architecture, 2002 — không phải GoF): sinh ra cho bài toán mà GoF chưa chạm tới — ranh giới giữa mô hình nghiệp vụ trong RAM và cơ sở dữ liệu. Cũng là bộ ba bị cargo-cult nhiều bậc nhất trong thế giới backend; chương này dựng chúng từ bài toán thật và vạch rõ ngưỡng lạm dụng. A. Repository 1. Bài toán — đã gặp, giờ gọi đúng tên Toàn bộ Level 2 đã xây nó từng viên gạch: ProductSource của pricing (2.5), FileStore với contract test (2.3), UserGetter phía consumer (2.4). Repository = tổng hợp các viên gạch đó thành pattern có tên: một interface nói ngôn ngữ domain đứng giữa nghiệp vụ và persistence, cho nghiệp vụ ảo tưởng có kiểm soát rằng aggregate nằm trong một collection trong RAM: ...

July 17, 2026 · 8 min

4.11 — Dependency Injection & DI Container: từ nguyên tắc đến công cụ, và ranh giới giữa hai thứ

1. Đặt lại nền — ba khái niệm hay bị trộn làm một Chương 2.5 đã cắm mốc, giờ dựng thành khung hoàn chỉnh vì toàn bộ chương này đứng trên đó: DIP (nguyên tắc) : chiều mũi tên — nghiệp vụ định nghĩa interface, hạ tầng implement DI (kỹ thuật) : cách trao dependency — tạo ở ngoài, đưa vào qua constructor/tham số DI Container (công cụ): máy tự động hóa việc trao — tự phân giải graph, tự gọi constructorBa tầng độc lập: dùng container cả ngày vẫn vi phạm DIP được (inject thẳng concrete hạ tầng vào domain); tuân DIP hoàn hảo không cần container nào (wiring tay ở main). Chương này trả lời câu hỏi thực dụng còn nợ: khi nào kỹ thuật cần leo lên công cụ — và cái giá của cú leo đó. ...

July 17, 2026 · 7 min

4.10 — Template Method, Visitor, Iterator & Memento: bốn số phận thời hậu GoF

Bốn pattern behavioral còn lại, điểm chung: ngôn ngữ hiện đại đã thay đổi số phận của chúng mạnh nhất — hai cái tan gần hết vào ngôn ngữ (Template Method, Iterator), một cái thu hẹp về ngách chuyên môn (Visitor), một cái sống dưới các tên khác (Memento). Chương này ngắn có chủ đích, ưu tiên “nhận ra và chọn đúng thay thế” hơn là “cài đặt nguyên bản”. ...

July 17, 2026 · 8 min

4.9 — Command, Chain of Responsibility & Mediator

Ba pattern behavioral về tổ chức lời gọi: Command biến lời gọi thành dữ liệu; Chain of Responsibility cho lời gọi chạy qua một hàng người xử lý; Mediator gom các lời gọi chéo nhau về một trung tâm điều phối. Phần A — Command 1. Bài toán — khi lời gọi hàm cần được cầm nắm Lời gọi hàm thường: xảy ra ngay, không lưu được, không xếp hàng được, không hoàn tác được, không gửi đi xa được. Một loạt nhu cầu thực tế đòi hỏi ngược lại: ...

July 17, 2026 · 7 min

4.8 — Observer & pub/sub: tách người gây chuyện khỏi người quan tâm

1. Problem Statement — code evolution Quay lại ConfirmOrder (1.2). Khi đó ta tách Notifier ra sau interface — nhưng service vẫn gọi notifier. Giờ nghiệp vụ phình theo hướng khác: sau khi xác nhận đơn, cần thêm nhiều việc ăn theo: // ❌ V1 — use case chính trở thành danh bạ của mọi bên quan tâm func (s *Service) ConfirmOrder(ctx context.Context, orderID string) error { /* ... load, confirm, save — NGHIỆP VỤ LÕI, 10 dòng ... */ // và cái đuôi mọc dài mãi: s.notifier.OrderConfirmed(ctx, o) // gửi mail s.loyalty.AddPoints(ctx, o.CustomerID, o.Total()) // cộng điểm s.analytics.Track(ctx, "order_confirmed", o) // sự kiện analytics s.recommender.InvalidateCache(ctx, o.CustomerID) // xóa cache gợi ý s.fraud.ReportSignal(ctx, o) // tín hiệu chống gian lận return nil } Quan sát bệnh sinh: mỗi bên quan tâm mới (team Loyalty, team Analytics…) = mở use case lõi ra sửa + thêm một dependency vào struct — trong khi nghiệp vụ “xác nhận đơn” không hề đổi. Mũi tên phụ thuộc trỏ sai chiều: module quan trọng nhất (order) phải biết mọi module ăn theo (analytics, recommender — những thứ đổi xoành xoạch). Vi phạm OCP theo trục “bên quan tâm”, vi phạm DIP về chiều ổn định. Và câu hỏi khó chịu bắt đầu xuất hiện: lỗi cộng điểm có nên làm hỏng việc xác nhận đơn không? — trong V1, có, dù chẳng ai chủ đích quyết như vậy. ...

July 17, 2026 · 8 min

4.7 — Strategy & State: hai anh em cùng cấu trúc, khác linh hồn

1. Strategy — pattern bạn đã dùng suốt từ đầu tài liệu Không cần dựng lại code evolution — bạn đã đi trọn con đường của nó ba lần: rule tính phí ship thành []FeeRule (1.1 → 1.4), gateway thanh toán thành interface + registry (2.2), rule hoa hồng thành []RateRule (3.4). Chỉ cần chính danh hóa: Intent (GoF): đóng gói một họ thuật toán thay thế được cho nhau, để thuật toán biến thiên độc lập với client dùng nó. Cấu trúc GoF: interface Strategy + N concrete class + context giữ tham chiếu strategy. ...

July 17, 2026 · 9 min

4.6 — Composite, Bridge & Flyweight: ba pattern cấu trúc còn lại

Ba pattern khép lại nhóm Structural, mỗi cái một số phận khác nhau trong backend hiện đại: Composite sống khỏe ở một ngách rõ ràng (cấu trúc cây); Bridge là ý tưởng quan trọng hơn hình thức; Flyweight chủ yếu đã chuyển hộ khẩu xuống tầng runtime và thư viện. Phần A — Composite 1. Bài toán — khi phần tử và nhóm phần tử phải cư xử như nhau Bài toán thật: hệ thống phân quyền theo nhóm. Ban đầu chỉ có user: ...

July 17, 2026 · 8 min

4.5 — Decorator & Proxy — và Middleware, đứa con lớn nhất của chúng

1. Vị trí xuất phát Decorator không cần giới thiệu dài: chương 1.4 đã phát minh lại nó khi tháo cây kế thừa Notifier — retry/rate-limit/logging thành các wrapper cùng interface, tổ hợp tự do. Chương này làm ba việc: gọi tên chính thức và nêu đủ cấu trúc; phân định với người anh em song sinh Proxy; và mổ Middleware — hình thái Decorator quan trọng nhất của backend hiện đại, kèm những bài học production mà bản đồ chơi ở 1.4 chưa chạm tới. ...

July 17, 2026 · 8 min

4.4 — Adapter & Facade: hai cách thuần hóa code không phải của mình

Mở màn nhóm Structural bằng cặp pattern hay bị dùng lẫn tên nhất. Cả hai đều đứng ở ranh giới giữa code của bạn và code không phải của bạn (thư viện, hệ legacy, service ngoài) — nhưng giải hai bài toán khác hẳn: Adapter đổi hình dạng một interface cho khớp ổ cắm có sẵn; Facade thu nhỏ cả một mê cung thành vài cánh cửa. Phần A — Adapter 1. Problem Statement — code evolution Hệ thống notification của bạn (từ chương 1.4) đã chuẩn hóa quanh interface: ...

July 17, 2026 · 9 min

4.3 — Singleton & Prototype: hai pattern thu hẹp đất sống

Hai pattern Creational còn lại thuộc nhóm số phận thứ ba của bản đồ 4.0: bài toán gốc của chúng hoặc đã có lời giải rẻ hơn, hoặc hiếm hơn xưa nhiều. Chương này ngắn hơn các chương khác một cách chủ đích — độ dài tỉ lệ với tần suất bạn nên dùng chúng. Phần A — Singleton 1. Bài toán gốc — và cú trượt dài nửa thế kỷ Bài toán chính đáng: một tài nguyên về bản chất chỉ nên tồn tại một trong process — connection pool, logger gốc, registry metric, config đã load. Tạo nhiều bản là lãng phí (N connection pool) hoặc sai (hai registry metric đếm lệch nhau). ...

July 17, 2026 · 8 min

4.2 — Builder & Functional Options: khởi tạo phức tạp mà không phát điên

1. Problem Statement Factory (4.1) giải bài toán chọn loại nào. Chương này giải bài toán trực giao: một loại duy nhất nhưng khởi tạo phức tạp — nhiều tham số, đa số tùy chọn, có ràng buộc chéo giữa các tham số, có giá trị mặc định thông minh. Nạn nhân điển hình: HTTP client, server, DB connection, message consumer — thứ nào cũng 15+ nút vặn. 2. Code Evolution — con đường đau khổ quen thuộc V1 — constructor phình dần (Massive Constructor, đã điểm mặt trong danh sách anti-pattern từ đầu tài liệu): ...

July 17, 2026 · 8 min

4.1 — Factory Method & Abstract Factory: kiểm soát điểm tạo object

1. Problem Statement Xâu chuỗi những gì đã có: chương 2.2 (OCP) đưa các phương thức thanh toán về interface Gateway + registry; chương 3.4 kết thúc Replace Conditional with Polymorphism bằng câu: “nơi tạo object từ string trở thành điểm switch duy nhất còn lại — và nó có tên: Factory”. Chương này trả lời nốt: điểm tạo object đó thiết kế thế nào cho tử tế — vì new (hay &Struct{}) chính là dạng coupling cứng nhất: nơi nào gọi &MomoGateway{...} là nơi đó dính chặt vào concrete type, biết cấu trúc field, chịu trách nhiệm truyền config. ...

July 17, 2026 · 9 min

4.0 — Design Patterns: cách đọc GoF bằng con mắt 2026

1. Pattern là gì — và cuốn sách GoF thực ra nói gì Năm 1994, bốn tác giả (Gang of Four) xuất bản Design Patterns: Elements of Reusable Object-Oriented Software — danh mục 23 cấu trúc lặp đi lặp lại mà họ quan sát thấy trong các hệ thống C++/Smalltalk tốt. Ý tưởng mượn từ kiến trúc sư Christopher Alexander: pattern = một giải pháp đã được kiểm chứng cho một bài toán lặp lại trong một ngữ cảnh cụ thể — kèm tên gọi chung để kỹ sư nói chuyện với nhau bằng một từ thay vì mười phút giải thích. ...

July 17, 2026 · 5 min

3.4 — Kỹ thuật refactoring cấu trúc: Replace Conditional with Polymorphism, Replace Inheritance with Composition, Replace Loop with Strategy

Ba kỹ thuật này khác nhóm 3.3 về tầm vóc: chúng không dọn dẹp bên trong một hàm/class mà đổi cấu trúc phân nhánh và quan hệ giữa các type — và điểm đến của chúng thường chính là một design pattern. Đây là cây cầu sang Level 4: pattern không phải thứ áp vào từ đầu, mà là hình dạng code sau khi các refactoring này hoàn thành. A. Replace Conditional with Polymorphism Bài toán nó giải: cùng một cấu trúc switch/if-else trên cùng một “loại” bị lặp ở nhiều hàm — mỗi lần thêm loại phải đi săn đủ các bản sao (đã phân tích tiêu chí đầy đủ ở OCP, chương 2.2). ...

July 17, 2026 · 9 min

3.3 — Kỹ thuật refactoring cơ bản: Extract Method, Move Method, Extract Class, Introduce Parameter Object

Bốn thao tác “dao kéo hằng ngày” — 90% công việc refactoring thực tế là bốn thao tác này lặp đi lặp lại. Mỗi kỹ thuật trình bày: khi nào dùng → các bước cơ giới (mechanics) → bẫy đặc thù → ví dụ Go. Phần mechanics quan trọng hơn vẻ ngoài của nó: refactoring an toàn là chuỗi bước nhỏ đến mức nhàm chán, mỗi bước giữ test xanh — không phải “đập ra sắp lại theo trí nhớ”. ...

July 17, 2026 · 8 min

3.2 — Code Smells nhóm coupling: Duplicate Code, Shotgun Surgery, Feature Envy, Message Chains, Middle Man

Nhóm smell này chung bệnh sinh: tri thức đặt sai chỗ — hoặc một tri thức bị nhân bản nhiều nơi, hoặc hành vi sống xa dữ liệu của nó, hoặc cấu trúc nội bộ bị phơi ra cho kẻ khác dựa vào. A. Duplicate Code Ví dụ — ba cấp độ, cấp sau khó thấy hơn cấp trước // Cấp 1 — trùng nguyên văn (dễ thấy, tool bắt được): // hai handler cùng 12 dòng parse-validate-load-user y hệt nhau // Cấp 2 — trùng cấu trúc, khác chi tiết: func exportOrdersCSV(orders []Order) []byte { /* mở buffer, ghi header, loop, format, flush */ } func exportUsersCSV(users []User) []byte { /* mở buffer, ghi header, loop, format, flush */ } // Cấp 3 — trùng TRI THỨC, code trông khác hẳn nhau (nguy hiểm nhất): // Rule "đơn >= 500k miễn ship" xuất hiện ở: // - Go backend: if total >= 500_000 { fee = 0 } // - TS frontend: total >= 500_000 ? "Miễn phí" : "20.000đ" // - SQL report: CASE WHEN total >= 500000 THEN 0 ... // - Chatbot config: "đơn từ 500 nghìn được freeship nha!" Nguyên nhân Copy-paste là đường đi ngắn nhất lúc viết; hai người viết cùng logic không biết nhau; và ranh giới hệ thống (frontend/backend/report) khiến trùng lặp tri thức gần như bắt buộc về mặt kỹ thuật — nhưng không ai ghi sổ để đồng bộ. ...

July 17, 2026 · 10 min

3.1 — Code Smells nhóm "phình to": Long Method, Large Class, God Object, Primitive Obsession, Data Clumps

Nhóm smell này chung một bệnh sinh: một đơn vị code (hàm, struct, kiểu dữ liệu) ôm nhiều hơn mức nó nên ôm — cohesion giảm dần mà không ai để ý, vì mỗi lần chỉ thêm “một tí”. Mỗi smell trình bày theo khung: ví dụ → nguyên nhân → hậu quả → refactoring phù hợp → khi nào KHÔNG phải smell. A. Long Method Ví dụ // ❌ handler 150 dòng — rút gọn còn khung xương, bạn đã thấy nó ở mọi codebase func (h *Handler) CreateOrder(w http.ResponseWriter, r *http.Request) { // ...15 dòng parse + validate JSON... // ...10 dòng check tồn kho, gọi inventory service... // ...20 dòng tính giá: giảm giá theo hạng thành viên, mã coupon, phí ship... // ...15 dòng ghi DB trong transaction, xử lý rollback... // ...10 dòng gửi event Kafka, log, metric... // ...10 dòng build response... } Nguyên nhân Không ai viết hàm 150 dòng — hàm 20 dòng lớn lên thành 150 qua hai năm, mỗi PR thêm 5-10 dòng “tiện thể nhét vào đây vì context sẵn rồi”. Lực hấp dẫn tự nhiên: thêm code vào hàm có sẵn luôn dễ hơn tạo cấu trúc mới. Long Method là default của entropy, không phải lỗi cá nhân. ...

July 17, 2026 · 9 min

3.0 — Technical Debt & Legacy Code: kinh tế học của việc refactor

1. Problem Statement Level 1–2 cho bạn lý thuyết về thiết kế tốt. Nhưng bạn không làm việc trên trang giấy trắng — bạn làm việc trên một codebase 4 năm tuổi, 200 nghìn dòng, viết bởi 15 người trong đó 10 người đã nghỉ, không có test, và đang chạy production nuôi sống công ty. Câu hỏi thực tế không phải “thiết kế đẹp trông thế nào” mà là: ...

July 17, 2026 · 9 min

2.6 — DRY, KISS, YAGNI, Law of Demeter, Tell Don't Ask

Năm nguyên tắc “dân gian” — được trích dẫn nhiều nhất và hiểu sai nhiều nhất. Chương này trả mỗi cái về đúng phát biểu gốc, chỉ ra dạng hiểu sai phổ biến, và quan trọng nhất: chúng mâu thuẫn nhau ở đâu và trọng tài là gì. A. DRY — Don’t Repeat Yourself Phát biểu gốc — và phần bị quên The Pragmatic Programmer (1999): “Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.” ...

July 17, 2026 · 9 min

2.5 — Dependency Inversion Principle: đảo chiều phụ thuộc

1. Problem Statement DIP là nguyên tắc kiến trúc nhất trong SOLID — nó không nói về một class hay một interface, mà về chiều của các mũi tên trong toàn hệ thống. Và nó là nền móng trực tiếp của Clean Architecture, Hexagonal Architecture (Level 5). Phát biểu gốc gồm hai vế: High-level modules không nên phụ thuộc low-level modules. Cả hai nên phụ thuộc abstraction. Abstraction không nên phụ thuộc chi tiết. Chi tiết nên phụ thuộc abstraction. “High-level” = chính sách, nghiệp vụ, lý do phần mềm tồn tại (“đơn hàng được xác nhận thì trừ kho và báo khách”). “Low-level” = cơ chế thực thi (Postgres, Kafka, SendGrid, HTTP). Trực giác nền (chương 1.3): mũi tên phải trỏ từ thứ hay đổi về thứ ổn định — và nghiệp vụ ổn định hơn hạ tầng: công ty đổi Redis sang Memcached, đổi REST sang gRPC thường xuyên hơn nhiều so với đổi “đơn xác nhận thì trừ kho”. ...

July 17, 2026 · 8 min

2.4 — Interface Segregation Principle: interface nhỏ, phía consumer

1. Problem Statement Câu chuyện gốc của ISP đáng kể lại vì nó rất “đời”: Robert Martin tư vấn cho Xerox, hệ thống điều khiển máy in đa năng. Mọi tác vụ (in, scan, staple, fax) đều đi qua một class Job khổng lồ. Task chỉ cần in cũng phụ thuộc Job — nên mỗi khi ai đó sửa phần staple, toàn bộ hệ thống recompile và redeploy (C++ thập niên 90: recompile tính bằng giờ). Thuốc của Martin: tách Job thành các interface theo từng client — PrintJob, StapleJob — mỗi client chỉ thấy phần nó dùng. ...

July 17, 2026 · 7 min

2.3 — Liskov Substitution Principle: hợp đồng hành vi

1. Problem Statement LSP nổi tiếng qua ví dụ Hình vuông/Hình chữ nhật, nhưng ví dụ đó khiến nhiều người nghĩ nó là chuyện hàn lâm về kế thừa. Thực tế, LSP là nguyên tắc thực chiến nhất với dev Go/TS hiện đại — vì nó trả lời câu hỏi: “khi nào một implementation của interface là ĐÚNG?” Phát biểu của Barbara Liskov (1987, diễn giải): nếu S là subtype của T, thì mọi chỗ dùng T phải hoạt động đúng khi thay bằng S — mà không cần biết đó là S. ...

July 17, 2026 · 8 min

2.2 — Open/Closed Principle: mở rộng mà không sửa

1. Problem Statement Bài toán quen thuộc đến mức mọi backend engineer đều từng viết nó: thanh toán qua nhiều cổng. Ngày đầu chỉ có COD. Rồi thêm Momo. Rồi VNPay, ZaloPay, thẻ quốc tế, trả góp… // V1 — hoàn toàn ổn khi có 2 phương thức func ProcessPayment(o *Order) error { switch o.PaymentMethod { case "cod": o.Status = "awaiting_delivery" return nil case "momo": return callMomoAPI(o) default: return fmt.Errorf("unsupported payment method: %s", o.PaymentMethod) } } 18 tháng sau, hàm này 400 dòng, 9 case, mỗi case gọi API khác nhau với retry/timeout khác nhau. Và điều tệ hơn: cùng cái switch o.PaymentMethod đó bị lặp ở 5 nơi khác — tính phí giao dịch, render UI text, xử lý refund, đối soát, webhook. Thêm ZaloPay nghĩa là tìm đủ 6 cái switch rải rác để thêm case — sót một cái là bug âm thầm (default nuốt lỗi hoặc panic ở production). ...

July 17, 2026 · 8 min

2.1 — Single Responsibility Principle: trục của sự thay đổi

1. Problem Statement SRP là nguyên tắc bị hiểu sai nhiều nhất trong SOLID. Cách hiểu sai phổ biến: “mỗi hàm/class chỉ làm một việc” → dẫn đến trò chẻ code thành trăm hàm 3 dòng, mỗi lần đọc luồng nghiệp vụ phải nhảy 15 file. Đó không phải SRP. Định nghĩa gốc của Martin: “A module should have one, and only one, reason to change.” — một module chỉ nên có MỘT LÝ DO ĐỂ THAY ĐỔI. ...

July 17, 2026 · 8 min

2.0 — SOLID: lịch sử ra đời và vấn đề nó thực sự giải quyết

1. Vấn đề lịch sử — SOLID sinh ra từ nỗi đau nào? SOLID không rơi từ trên trời xuống. Nó là sản phẩm của một thời điểm lịch sử rất cụ thể — và hiểu bối cảnh đó quyết định việc bạn áp dụng nó đúng hay mù quáng. Thập niên 1990, thế giới C++/OOP doanh nghiệp. Các hệ thống lớn viết bằng C++ thời đó có đặc điểm: biên dịch một lần mất hàng giờ; sửa một header file kéo theo recompile cả dây chuyền phụ thuộc; deploy là chép binary khổng lồ; và cây kế thừa sâu là “best practice” được dạy chính thống. Robert C. Martin (Uncle Bob) quan sát các codebase như vậy và mô tả bốn triệu chứng của “thiết kế mục nát” (design rot): ...

July 17, 2026 · 5 min

1.5 — Interface & Immutability: hai vũ khí thiết kế của Go

Phần A — Interface 1. Problem Statement Interface đã xuất hiện ở mọi chương trước như “công cụ giảm coupling”. Chương này trả lời các câu hỏi mà kỹ sư senior thực sự vướng: interface đặt ở đâu? To hay nhỏ? Khai báo khi nào? Vì sao interface Go khác Java/TS ở tầng triết lý — và điều đó đổi cách thiết kế ra sao? 2. Điều làm interface Go khác biệt: implicit satisfaction package storage type Saver interface { Save(ctx context.Context, key string, data []byte) error } Bất kỳ type nào có method Save đúng chữ ký — tự động thỏa mãn Saver. Không implements, không import package chứa interface. Hệ quả thiết kế sâu sắc: ...

July 17, 2026 · 10 min

1.4 — Composition vs Inheritance & Polymorphism

1. Problem Statement Bài toán: hệ thống có nhiều loại thông báo — Email, SMS, Push. Chúng chia sẻ logic chung (retry, logging, rate-limit) nhưng khác nhau ở cách gửi. Làm sao tái sử dụng phần chung mà không copy-paste? Đây là bài toán mà inheritance (kế thừa) sinh ra để giải. Và cũng là bài toán cho thấy vì sao inheritance thường là câu trả lời sai — đến mức chính GoF, trong cuốn sách 1994, đã viết ở trang 20: “Favor object composition over class inheritance”. Go đi xa hơn: loại bỏ hẳn class inheritance khỏi ngôn ngữ. ...

July 17, 2026 · 10 min