# Vòng system design của FDE: thiết kế quanh hệ thống legacy của khách, không xây lại từ đầu

> Tin tuyển FDE nói rõ bạn sẽ làm việc trong những quyết định người khác đã đưa ra, nên một kiến trúc vẽ trên giấy trắng là kiến trúc cho một khách hàng không tồn tại.

Bản gốc: https://fdetimes.net/vi/bach-khoa/system-design-fde-ha-tang-legacy-on-prem/

Thử hình dung đề bài: khách hàng là một công ty bảo hiểm có hệ thống quản lý hợp đồng chạy on-prem nhiều năm nay, đội vận hành không cho ai sửa. Họ muốn một service phân loại hồ sơ bồi thường. Bạn có 45 phút và một tấm bảng trắng.

Phản xạ của nhiều kỹ sư là vẽ ngay một kiến trúc sạch: API gateway, microservice, database mới, rồi đề xuất "dần dần chuyển hết sang". Ở vòng SWE thông thường, câu trả lời đó có thể đủ. Với một vị trí FDE, nó bỏ qua đúng phần việc mà tin tuyển dụng mô tả.

Tin tuyển FDE của Praxent ghi rõ yêu cầu tích hợp gọn gàng với những hệ thống bạn thực sự được giao, kể cả các core legacy, và nói thẳng rằng bạn làm việc bên trong những quyết định do người khác đưa ra.

Vì thế, cách chuẩn bị hợp lý là giả định người phỏng vấn muốn xem bạn thiết kế quanh ràng buộc ra sao, chứ không phải bạn xóa ràng buộc giỏi đến đâu.

**Điểm mấu chốt:** Ở hiện trường, ràng buộc của khách không cản trở thiết kế; nó chính là đề bài.

## Vì sao "viết lại cho sạch" là câu trả lời sai?

Martin Fowler cảnh báo về kế hoạch viết lại một lần cho xong: nghe đơn giản, nhưng theo quan sát của ông, phần lớn những kế hoạch như vậy kết thúc bằng thất bại. Phương án ông ủng hộ là hiện đại hóa từng phần, và nó có tên: Strangler Fig.

Theo tài liệu kiến trúc Azure của Microsoft, Strangler Fig thay dần từng phần chức năng của hệ thống cũ bằng ứng dụng và service mới. Trong lúc đó, hệ thống cũ vẫn chạy những chức năng chưa được chuyển. Cơ chế cốt lõi là một façade, tức một lớp proxy, đứng chặn trước back end cũ và quyết định mỗi request đi vào ứng dụng cũ hay service mới.

Nhưng pattern nào cũng có điều kiện. Microsoft ghi rõ Strangler Fig có thể không phù hợp nếu bạn không truy cập được mã nguồn hệ thống cũ, đồng thời cũng không có điểm tích hợp nào khác được phép thay đổi, chẳng hạn database. Đó chính là câu hỏi đầu tiên bạn nên đặt ra trong buổi phỏng vấn.

## Bắt đầu bằng câu hỏi, đừng vội vẽ

Với đề bảo hiểm ở trên, năm phút đầu nên dành để hỏi. Code của hệ thống hợp đồng có sửa được không? Nếu không, có được đọc database của nó không? Mạng nội bộ cho phép kết nối theo chiều nào? Ai sẽ duyệt quyền truy cập, và thường mất bao lâu?

Câu cuối không thừa. Appsierra, một công ty cung cấp FDE, cho rằng cấp quyền truy cập, review bảo mật và phê duyệt của khách thường mất nhiều thời gian hơn cả việc tìm được kỹ sư.

Đó là nhận định của nhà cung cấp chứ không phải dữ liệu độc lập, nhưng nếu bạn đưa thời gian review vào kế hoạch, bạn cho thấy mình hiểu dự án thực sự có thể bị chậm ở đâu.

Giả sử người phỏng vấn trả lời: không được sửa code, được đọc database, firewall không mở chiều vào. Ba câu trả lời đó quyết định gần hết kiến trúc.

## Ví dụ mẫu: dựng đường dữ liệu từng lớp một

Bắt đầu từ mạng. Khi firewall không mở chiều vào, bạn đặt một agent trong mạng của khách và cho nó chỉ kết nối ra ngoài. Palantir Foundry từng làm đúng như vậy: agent trong mạng khách liên tục poll Foundry qua kết nối HTTPS một chiều đi ra để nhận việc.

Tài liệu của họ hiện đánh dấu kiến trúc agent worker này là legacy, nhưng nguyên lý không mở cổng chiều vào vẫn đáng học theo khi bạn vẽ phương án.

Tiếp theo là cách lấy sự kiện. Vì không được sửa code, bạn không thể chèn lệnh "gửi event khi có hồ sơ mới" vào ứng dụng. Với những thành phần không sửa được, Microsoft gợi ý lấy sự kiện từ chính database, ví dụ dùng change data capture đọc transaction log, để không phải đụng tới code ứng dụng.

Sự kiện đã ra được tới nơi, nhưng nó vẫn mang hình dạng của hệ thống cũ. Microsoft cảnh báo hệ thống legacy thường có schema rối và API lỗi thời. Nếu không có lớp cô lập, thiết kế mới sẽ dần nhiễm ngữ nghĩa của hệ thống cũ. Lớp cô lập đó gọi là anti-corruption layer (ACL).

Thử hình dung bảng hồ sơ cũ có các cột viết tắt, mã trạng thái dạng số và số tiền lưu dưới dạng số nguyên nhân 100. ACL là nơi duy nhất biết những quy ước đó:

```python
# ACL: dịch bản ghi legacy sang mô hình của service mới
STATUS_MAP = {"01": "submitted", "02": "in_review", "09": "closed"}

def to_claim(row: dict) -> Claim:
    return Claim(
        claim_id=row["CLM_NO"].strip(),
        policy_id=row["POL_REF"].strip(),
        status=STATUS_MAP.get(row["ST_CD"], "unknown"),
        amount=Decimal(row["AMT_X100"]) / 100,
    )
```

Service phân loại chỉ làm việc với `Claim` và không bao giờ đọc `ST_CD`. Đây cũng là phần việc nặng ngoài đời: Appsierra mô tả công việc tích hợp của FDE là đối chiếu schema, cách đặt tên và những chỗ thiếu dữ liệu thật của khách với những gì sản phẩm cần.

Cuối cùng là façade. Ngày đầu, chỉ một đường dẫn được chuyển sang service mới, mọi thứ khác vẫn đi vào hệ thống cũ:

```python
MIGRATED = {"/claims/triage"}

def route(path: str) -> str:
    return NEW_SERVICE if path in MIGRATED else LEGACY
```

Mỗi lần chuyển thêm một chức năng chỉ là thêm một dòng vào `MIGRATED`, và lùi lại cũng dễ như vậy.

## Mỗi lớp đều có giá, hãy nói ra trước

Hãy tự nêu chi phí trước khi bị hỏi. Microsoft ghi rõ ACL làm tăng độ trễ cho các lời gọi giữa hai hệ thống. Nếu bước phân loại phải trả kết quả ngay cho nhân viên đang nghe điện thoại, bạn phải tính phần độ trễ đó vào ngân sách.

Nếu hệ thống cũ chạy theo batch hoặc chậm, bạn có thêm một lựa chọn: ACL dạng event-driven qua một hàng đợi như Service Bus, Event Grid hay Event Hubs.

Theo Microsoft, cách này tách phần hệ thống mới khỏi giới hạn throughput của hệ thống cũ. Cái giá là dữ liệu đến chậm hơn và chỉ nhất quán sau một khoảng trễ (eventual consistency), và bạn nên nói rõ điều đó.

## Những cái bẫy nên tránh

Cái bẫy đầu tiên là áp Strangler Fig mà không kiểm tra điều kiện. Nếu khách không cho chạm vào code lẫn database, façade không có chỗ để bám, và bạn phải nói thẳng điều đó thay vì cố nhét pattern vào.

Một cái bẫy khác là để ACL chỉ có tên trên sơ đồ. Nếu một service khác ngoài ACL vẫn đọc mã trạng thái cũ, ngữ nghĩa legacy đã lọt vào hệ thống mới. Và đừng quên con người: không ai duyệt quyền truy cập thì kiến trúc đẹp mấy cũng nằm yên trên giấy.

Về sự nghiệp, khi đọc tin tuyển dụng, những cụm như "legacy cores", "on-prem" hay "existing systems" là gợi ý tốt về nội dung vòng system design. Trong CV, đừng chỉ liệt kê công nghệ. Hãy viết theo kiểu "đưa dữ liệu từ hệ thống X qua CDC, không cần sửa code phía khách", vì đó đúng là loại tích hợp mà các tin tuyển FDE mô tả.

Bài tập tuần này rất đơn giản: lấy đề công ty bảo hiểm ở trên, đổi một câu trả lời thành "không được đọc database", rồi giải lại. Nếu bạn vẫn giải thích được vì sao kiến trúc phải đổi, bạn đã luyện đúng kỹ năng mà tin tuyển FDE đang tìm.

**Thử ngay tuần này:**

- Chọn một hệ thống cũ ở công ty bạn rồi trả lời bốn câu: sửa được code không, chạm được database không, mạng cho phép kết nối chiều nào, ai phải duyệt quyền truy cập.
- Tự giải đề phân loại hồ sơ bồi thường trong 30 phút, bấm giờ, và bắt buộc dành 5 phút đầu chỉ để hỏi về ràng buộc.
- Viết lại một dòng trong CV theo mẫu: tích hợp với hệ thống nào, qua điểm nối nào, không phải sửa gì ở phía khách.

## Nguồn

- [Strangler Fig Pattern - Azure Architecture Center | Microsoft Learn](https://learn.microsoft.com/en-us/azure/architecture/patterns/strangler-fig)

- [Anti-Corruption Layer Pattern - Azure Architecture Center | Microsoft Learn](https://learn.microsoft.com/en-us/azure/architecture/patterns/anti-corruption-layer)

- [Strangler Fig Application (Martin Fowler)](https://martinfowler.com/bliki/StranglerFigApplication.html)

- [Agent architecture - Palantir Foundry documentation](https://palantir.com/docs/foundry/data-connection/architecture)

- [Forward Deployed Engineers - Appsierra](https://www.appsierra.com/services/forward-deployed-engineers)

- [Forward Deployed Engineer at Praxent | Torre](https://torre.ai/post/6WZzzL0W-praxent-forward-deployed-engineer)
