Domain-Driven Agents: Chiến lược đưa AI vào dự án phần mềm cũ

Phần mềm29 tháng 8, 2026·8 phút đọc

Bài viết phân tích cách áp dụng mô hình Domain-Driven Design (DDD) để nâng cao hiệu quả của các tác nhân AI (agents) trong các dự án phần mềm đã tồn tại lâu năm (brownfield). Tác giả đề xuất tách công việc thành chiến lược (strategic) và chiến thuật (tactical), đồng thời sử dụng các công cụ như context map và glossary để định nghĩa ngôn ngữ thống nhất, giúp AI hiểu đúng hệ thống và giảm thiểu sai sót.

Domain-Driven Agents: Chiến lược đưa AI vào dự án phần mềm cũ

Domain-Driven Agents: Chiến lược đưa AI vào dự án phần mềm cũ

Trong bối cảnh trí tuệ nhân tạo (AI) đang len lỏi vào từng ngóc ngách của ngành công nghệ, việc ứng dụng các tác nhân AI (agents) vào các dự án phần mềm mới (greenfield) đã trở nên quen thuộc. Tuy nhiên, thách thức thực sự nằm ở việc áp dụng chúng vào các codebase cũ kỹ, phức tạp và tồn tại nhiều khoản nợ kỹ thuật (tech debt). Bài viết này sẽ trình bày một phương pháp tiếp cận dựa trên Domain-Driven Design (DDD) để giải quyết bài toán đó.

Tác giả nhận định rằng chất lượng công việc của LLM (Large Language Model) giảm mạnh khi được yêu cầu làm việc với các hệ thống cũ. Nguyên nhân không phải do mô hình yếu, mà do codebase thiếu một ngôn ngữ chung, nhất quán để mô tả các khái niệm nghiệp vụ. Bằng cách xây dựng một lớp ngữ nghĩa rõ ràng, chúng ta có thể biến AI từ một "thợ gõ phím" không đáng tin cậy thành một kỹ sư phần mềm thực thụ, có thể phối hợp nhịp nhàng trong quy trình phát triển.

Thực trạng: Khi AI đối mặt với "mớ hỗn độn" của dự án cũ

Việc sử dụng AI trong lập trình đã mang lại những lợi ích to lớn về năng suất, đặc biệt là ở các dự án mới hoặc quy mô nhỏ. Tuy nhiên, khi đưa AI vào các hệ thống đã hoạt động hàng năm trời, với đồ thị phụ thuộc chằng chịt và sự gắn kết (coupling) mạnh mẽ, chất lượng công việc giảm sút rõ rệt.

Lỗi thường gặp có một hình thái cụ thể. Ví dụ, khi yêu cầu AI thêm trường "trạng thái đơn ứng tuyển" trong một repo mới, mô hình làm rất tốt. Nhưng nếu yêu cầu trong một hệ thống đã phát triển bốn năm, mô hình có thể tạo ra cách viết thứ tư cho một khái niệm đã tồn tại ba lần, bởi chính codebase chưa bao giờ quyết định đâu là chuẩn. Nó có thể viết một adapter trong khi lẽ ra chỉ cần một lời gọi hàm, hoặc ngược lại. Mỗi sai sót đều là một câu hỏi về hệ thống mà hệ thống không có câu trả lời ở bất kỳ đâu. Mô hình phải đoán, và thường đoán sai.

Vấn đề nằm ở độ sâu của dự án. Độ phức tạp kỹ thuật chỉ là lớp đầu tiên. Bên dưới là một lớp thứ hai: sự mơ hồ, thiếu ý nghĩa và không có ngôn ngữ chung để giải quyết. Đó chính là lớp mà các mô hình AI thường "sập bẫy". Mô hình không phải là thứ cần nâng cấp. Chính codebase chưa sẵn sàng, và sự sẵn sàng đó là thứ chúng ta có thể xây dựng, từng phần một.

Chiến lược và chiến thuật: Phân chia công việc để tối ưu hóa AI

Một phần tư ngân sách công nghệ thường được dành để trả nợ kỹ thuật. Chi phí này gồm hai phần: quyết định (thay đổi gì và tại sao) và gõ code (thực hiện thay đổi). Chi phí cho việc gõ code đã giảm mạnh nhờ AI, nhưng chi phí cho việc quyết định vẫn gần như không đổi.

Dựa trên điều này, tác giả vay mượn khái niệm chiến lược (strategic)chiến thuật (tactical) từ cuốn "A Philosophy of Software Design" của John Ousterhout. Tuy nhiên, ông dùng chúng để phân chia quyền tác giả (authorship):

  • Công việc chiến lược: Đọc hiểu hệ thống, xác định điều gì cần thay đổi và tại sao, đồng thời đảm bảo sự thay đổi phục vụ đúng tính năng. Đây là phần cần có "hệ thống trong đầu" và nơi con người tham gia toàn diện.
  • Công việc chiến thuật: Chuyển những quyết định chiến lược thành các thay đổi cụ thể trong tệp tin. Đây là phần đã trở nên rẻ hơn nhờ AI.

Với cách tiếp cận này, các GitHub issue được tạo ra sau quá trình phân tích. Sau đó, các hệ thống AI sẽ xử lý các issue này dựa trên các kỹ năng (skills)tiểu tác nhân (sub-agents). Một kỹ năng là một quy trình được viết bằng Markdown, được mô hình tải lên khi phù hợp với nhiệm vụ. Một tiểu tác nhân là một phiên mô hình riêng với ngữ cảnh mới và nhiệm vụ cụ thể (triển khai, rà soát bảo mật...), báo cáo kết quả cuối cùng.

Con người đóng vai trò điều phối viên: lập kế hoạch, rà soát các Pull Request (PR) và phê duyệt. Thời gian tiết kiệm được là rất lớn.

DDD và Context Map: Nền tảng cho một ngôn ngữ thống nhất

Domain-Driven Design (DDD) của Eric Evans cung cấp một cách thu hẹp khoảng cách giao tiếp giữa phía nghiệp vụ và phía kỹ thuật thông qua ngôn ngữ chung (ubiquitous language)bối cảnh có ranh giới (bounded contexts). Với sự tham gia của các tác nhân AI, mối liên kết này càng trở nên quan trọng, giúp chúng ta diễn đạt nhu cầu và hiểu được logic của AI một cách chính xác.

Strategic vs. Tactical SplitStrategic vs. Tactical Split

Ý tưởng chính là mỗi repository đều có một tệp .workflow.json ở thư mục gốc. Tệp này đóng vai trò như một "bản tuyên ngôn", khai báo về ngôn ngữ lập trình, các thư mục AI nên đọc trước, và các kiểm tra cần vượt qua. Trong đó, một khối (block) quan trọng dành riêng cho miền (domain), khai báo tên dự án, các bounded contexts, nơi lưu trữ glossary của từng context, loại subdomain, và mọi kết nối với context lân cận.

Ví dụ, trong một hệ thống quản lý đơn ứng tuyển (job-offer-box), tệp manifest của frontend (web) khai báo kết nối đến backend (hyperion). Nó chỉ rõ hướng gọi (outbound), người sở hữu mô hình (nhà cung cấp - backend), và mẫu quan hệ (pattern) là "unclassified" vì vừa tuân thủ (conformist) khi ghi, vừa dùng lớp chống tham nhũng (ACL) khi đọc. Mô tả chi tiết này giúp AI hiểu rõ vai trò kép của các adapter.

Ubiquitous Language MapUbiquitous Language Map

Bên cạnh manifest, mỗi bounded context có một tệp CONTEXT.md đóng vai trò là glossary sống, định nghĩa chính xác từng thuật ngữ và các từ đồng nghĩa bị loại bỏ. Không có gì ở trên được soạn thảo thủ công. Context Map (bản đồ bối cảnh) - tài liệu tổng hợp mọi context và mọi kết nối - được tạo ra tự động bởi một script. Script này quét các repo, hợp nhất các khối domain và tạo ra tệp CONTEXT-MAP.md. Bản đồ này có thể tái tạo bất cứ lúc nào.

Kiểm tra chéo và tự động hóa: Khi mọi mối quan hệ đều được xác minh

Mỗi kết nối (edge) được khai báo hai lần, một từ mỗi phía. Sự trùng lặp này chính là chìa khóa. Script tạo ra context map sẽ kiểm tra chéo các cặp khai báo này. Sự khác biệt giữa những gì nhà cung cấp (supplier) khai báo và những gì người tiêu dùng (consumer) khai báo là một "phát hiện" (finding). Ví dụ, nhà cung cấp có thể khai báo "published-language" trong khi người tiêu dùng lại khai báo "conformist" hoặc "anticorruption-layer".

Context Map ChecksContext Map Checks

Script này được chạy như một kỹ năng (skill) trong ba thời điểm: khi chạm vào manifest, khi onboarding một repo mới, và trước khi thay đổi bất cứ điều gì mà context khác phụ thuộc vào. Mỗi sự không khớp sẽ được tạo thành một DDD issue trên repo sở hữu phía sai. Issue này có dấu vân tay (fingerprint) duy nhất, giúp các lần chạy sau cập nhật issue tương tự thay vì tạo mới, và các lỗi đã sửa sẽ tự động đóng issue.

Tương lai: Hướng tới một domain model thực thụ

Chiến lược này đã thiết lập được "hình dạng của bản đồ", xác định nơi một context kết thúc và cách nó giao tiếp với các context lân cận. Tuy nhiên, bên trong mỗi context vẫn là code thông thường, cho phép tạo ra các đối tượng vô nghĩa.

Context Map LoopContext Map Loop

Bước tiếp theo là tập trung vào chính codebase: lần lượt chuyển đổi từng context sang một mô hình miền thực thụ (domain model) dựa trên các thành phần DDD như value objects, aggregates, domain services. Quá trình này sẽ làm cho codebase trả lời được những câu hỏi mà mô hình AI từng phải đoán: từ này nghĩa là gì, ai sở hữu nó, phạm vi của context này đến đâu. Điều này hứa hẹn sẽ nâng cao đáng kể chất lượng và độ tin cậy của các tác nhân AI trong các dự án phần mềm phức tạp, thực tế.

Chia sẻ:FacebookX
Nội dung tổng hợp bằng AI, mang tính tham khảo. Xem bài gốc ↗