Khi ERP không có API tử tế: cách FDE tích hợp mà không kéo theo cả hệ thống cũ
Lối tắt nhanh nhất vào một hệ thống cũ thường là lối gỡ ra đắt nhất, vì vậy cần chọn cửa vào trước khi bắt đầu viết code.
- 1Kiểm kê cửa vàoDatabase, transaction log, IDoc/file, API, giao diện; ai sở hữu và lỗi báo về đâu
- 2Chọn đường đọcƯu tiên change data capture đọc transaction log, không truy vấn thẳng bảng
- 3Dựng anti-corruption layerMột nơi duy nhất biết tên cột cũ; giá trị lạ thì báo lỗi, không đoán
- 4Chọn đường ghiKênh bất đồng bộ có xử lý lại lỗi và giám sát, như IDoc khi ghi số lượng lớn
- 5Thay thế dầnDùng facade kiểu Strangler Fig nếu chặn được request; nếu không, cân nhắc CDC để đọc
Chọn cửa vào trước, đặt lớp dịch ở giữa, rồi mới viết tính năng.
Đồ hoạ: FDE Times
Tóm tắt nhanh
- Truy cập database trực tiếp là cách rẻ nhất lúc làm và đắt nhất lúc gỡ. Hãy coi nó là lựa chọn cuối cùng, không phải lựa chọn mặc định.
- Anti-corruption layer giữ mô hình dữ liệu cũ bên ngoài code mới. Đổi lại, bạn có thêm độ trễ và thêm một service phải vận hành.
- Đọc qua change data capture, ghi qua kênh bất đồng bộ có công cụ xử lý lỗi. Bot thao tác giao diện chỉ dùng khi không còn cửa nào khác.
Thử hình dung tuần thứ hai của bạn ở nhà máy khách hàng. Agent cần đọc đơn mua hàng và tạo phiếu nhập kho, nhưng ERP của họ chạy từ hơn mười năm trước. Tài liệu API không có ai giữ, còn anh quản trị database thì vui vẻ đưa bạn một tài khoản đọc thẳng vào bảng.
Gần như FDE nào cũng gặp tình huống này, và quyết định trong mấy ngày đầu sẽ đi theo dự án rất lâu. Cái khó không nằm ở chỗ kết nối được, mà ở chỗ kết nối sao cho mười tám tháng sau, khi ERP được nâng cấp, sản phẩm của bạn không sụp theo.
Cửa dễ vào nhất thường khó ra nhất
Tài liệu kiến trúc của Microsoft mô tả đúng vấn đề: hệ thống cũ hay có schema rối và API lỗi thời. Nếu hỗ trợ chúng một cách ngây thơ, ứng dụng mới sẽ bị ép dùng luôn ngữ nghĩa của hệ thống cũ.
Dấu hiệu dễ thấy nhất là những dòng như if STAT_CD == "02" bắt đầu xuất hiện rải rác trong code mới, và lúc đó sản phẩm của bạn đã bị buộc chặt vào ERP.
Vì thế, cái tài khoản đọc database kia đáng dè chừng hơn vẻ ngoài của nó. Một bài phân tích của Netguru về tích hợp hệ thống cũ nói thẳng rằng truy cập database trực tiếp là mẫu tích hợp rẻ nhất để dựng và đắt nhất để gỡ. Nó nhanh trong demo.
Sau đó mỗi lần đổi một cột là một lần sửa lại, mà không ai báo trước cho bạn.
Câu trả lời không phải là từ chối mọi lối tắt. Câu trả lời là kiểm kê mọi cửa vào trước, rồi chọn cửa riêng cho việc đọc và cho việc ghi.
| Cửa vào | Hợp với | Cái giá phải trả |
|---|---|---|
| Đọc/ghi thẳng database | Prototype trong vài ngày | Code bám chặt vào schema, rất đắt khi gỡ |
| Change data capture (đọc transaction log) | Đường đọc khi ứng dụng cũ không sửa được | Phải hiểu log của từng loại database |
| Kênh bất đồng bộ kiểu SAP IDoc | Ghi số lượng lớn | Không có kết quả tức thì |
| API đồng bộ (OData, RFC) | Tra cứu, ghi từng bản ghi | Phụ thuộc throughput của hệ thống cũ |
| Bot thao tác giao diện | Khi không còn cửa nào khác | Hỏng mỗi khi giao diện đổi, lỗi khó chẩn đoán |
Đường đọc và đường ghi cần cửa khác nhau
Đường đọc và đường ghi có rủi ro khác nhau, nên hãy tách chúng ra. Với đường đọc, khi ứng dụng cũ xoay quanh database và không thể sửa, Microsoft gợi ý dùng trigger trên chính database hoặc change data capture đọc transaction log để đẩy thay đổi sang service mới.
Trong hai cách đó, change data capture đáng ưu tiên hơn: vì nó đọc transaction log, bạn không phải truy vấn hay gắn gì vào các bảng nghiệp vụ của ERP.
Đường ghi thì nhạy cảm hơn, vì ghi sai vào ERP nghĩa là sai sổ sách. Lấy SAP làm ví dụ. Một phân tích trên aixsap cho rằng IDoc hợp với việc ghi số lượng lớn theo kiểu bất đồng bộ, vì nó có sẵn cơ chế xử lý lại lỗi và giám sát, trong khi OData là đồng bộ.
Cùng nguồn đó nhận xét rằng RFC vẫn còn dùng nhưng phạm vi đang hẹp lại, và phía gọi phải cài connector riêng.
Bài học rộng hơn SAP: hãy ưu tiên kênh mà đội vận hành của khách đã biết cách theo dõi. Khi lúc 2 giờ sáng có một phiếu nhập kho bị kẹt, họ cần một màn hình quen thuộc để xử lý lại. Một script bạn để lại thì không giúp được họ.
Lớp dịch: nơi duy nhất biết tên cột cũ
Dù đi bằng cửa nào, dữ liệu cũng phải đi qua một ranh giới. Microsoft gọi đó là anti-corruption layer (ACL): một lớp dịch giúp một hệ thống giữ nguyên mà không làm hỏng thiết kế và công nghệ của hệ thống kia. Netguru định nghĩa gọn hơn: ACL giữ mô hình dữ liệu của hệ thống cũ bên ngoài code mới.
Dưới đây là ví dụ cho đơn mua hàng. Tên cột là giả định, nhưng kiểu dữ liệu thì rất quen với ai từng làm ERP: mã trạng thái là số, mã nhà cung cấp có số 0 ở đầu, tiền lưu theo đơn vị nhỏ nhất, ngày lưu dạng chuỗi.
# acl/purchase_order.py — file DUY NHẤT được biết tên cột của ERP
from dataclasses import dataclass
from datetime import date, datetime
from decimal import Decimal
class UnknownLegacyValue(Exception):
pass
@dataclass(frozen=True)
class PurchaseOrder: # mô hình của ứng dụng mới
order_id: str
supplier_id: str
amount: Decimal
due_date: date
status: str # "open" | "approved" | "cancelled"
STATUS_MAP = {"01": "open", "02": "approved", "09": "cancelled"}
def from_legacy(row: dict) -> PurchaseOrder:
code = row["STAT_CD"]
if code not in STATUS_MAP:
# dừng lại ngay, không đoán
raise UnknownLegacyValue(f"STAT_CD={code!r} PO={row['PO_NO']}")
return PurchaseOrder(
order_id=row["PO_NO"].strip(),
supplier_id=row["VEND_CD"].lstrip("0"),
amount=Decimal(row["AMT"]) / 100,
due_date=datetime.strptime(row["DUE_DT"], "%Y%m%d").date(),
status=STATUS_MAP[code],
)
Đoạn code này ngắn, nhưng có ba điểm đáng chú ý. Agent và mọi service phía sau chỉ thấy PurchaseOrder, không bao giờ thấy STAT_CD. Gặp một mã trạng thái lạ thì nó báo lỗi rõ ràng chứ không lặng lẽ gán giá trị mặc định. Và khi ERP được thay, bạn chỉ phải viết lại đúng một file.
ACL không miễn phí. Microsoft nói rõ lớp này thêm độ trễ và thêm một service bạn phải quản lý, bảo trì. Khi hệ thống cũ chậm, có thể cho ACL chạy bất đồng bộ, dựa trên sự kiện: phần nghiệp vụ mới không còn phải chờ theo tốc độ của hệ thống cũ, vì dữ liệu được dịch qua message.
Cách này hợp với những luồng tải lớn hoặc cần tách rời hẳn hai hệ thống.
Năm bước làm ở hiện trường
Bước đầu là kiểm kê. Liệt kê mọi cửa vào như ở bảng trên, ghi ai sở hữu từng cửa và mỗi cửa báo lỗi ra sao. Câu hỏi nên đặt cho khách là “khi đồng bộ này hỏng, ai được báo và họ nhìn vào đâu?”, chứ không chỉ “có API không?”.
Tiếp theo, tách đường đọc khỏi đường ghi và chọn cửa riêng cho từng đường. Nên ưu tiên change data capture để đọc và kênh bất đồng bộ có công cụ giám sát để ghi. Bước thứ ba là viết ACL trước khi viết tính năng, kèm test cho từng giá trị lạ bạn gặp trong dữ liệu thật.
Bước thứ tư là nghĩ đến chuyện thay thế dần. Martin Fowler kể rằng ông và đồng nghiệp chọn hiện đại hóa từng bước thay vì viết lại một lần. Mẫu Strangler Fig dùng một facade, tức một proxy, chặn request đi vào hệ thống cũ rồi chuyển nó sang ứng dụng cũ hoặc service mới.
Nhưng Microsoft cũng lưu ý rằng mẫu này không phù hợp khi không chặn được request vào back end, mà với những ERP đóng kín thì đó là chuyện thường. Gặp trường hợp ấy, lựa chọn hợp lý là quay về change data capture cho đường đọc và chấp nhận tiến chậm hơn ở đường ghi.
Bước cuối là ghi lại quyết định: chọn cửa nào, vì sao, và cái giá chấp nhận trả là gì. Người tiếp quản sau bạn sẽ cần tài liệu này hơn bất cứ thứ gì khác.
Những cái bẫy quen thuộc
Bẫy đầu tiên là để lối tắt của bản demo thành kiến trúc thật. Câu truy vấn thẳng vào bảng viết trong tuần đầu vẫn có thể còn đó lúc go-live, chỉ vì không ai có thời gian quay lại sửa.
Bẫy thứ hai là coi bot giao diện là “tích hợp”. Workato cảnh báo rằng bot kiểu này dễ hỏng khi giao diện ứng dụng thay đổi. Activepieces chỉ ra điểm khác biệt quan trọng hơn: API trả về status code cho biết chính xác vì sao một tiến trình thất bại, còn bot chỉ đoán vị trí nút bấm.
Nếu buộc phải dùng bot, hãy đặt nó sau ACL để có thể thay nó đi mà không ảnh hưởng phần còn lại.
Bẫy thứ ba là dịch dữ liệu một cách lạc quan. Gán mặc định cho một mã trạng thái không ai biết nghĩa là cách nhanh nhất để agent tự tin làm sai trên dữ liệu thật của khách.
Khi đọc JD của một vị trí FDE, những cụm như “integrate with customer ERP” hay “legacy systems” chính là phần việc này. Trong CV, đừng chỉ viết “tích hợp SAP”. Hãy ghi bạn chọn cửa vào nào, vì sao, và cách bạn xử lý lỗi đồng bộ.
Bài tập tuần này đi xa hơn đường đọc: viết hàm ngược to_legacy cho chính thực thể bạn đã dịch, rồi viết một test round-trip. Lấy một bản ghi thật, dịch sang mô hình mới, dịch ngược lại và so từng trường với bản gốc.
Chỗ nào lệch, như số 0 ở đầu mã nhà cung cấp, đơn vị tiền hay định dạng ngày, chính là chỗ đường ghi sẽ làm sai sổ sách của khách nếu bạn không bắt được trước.
7 nguồn
- Anti-Corruption Layer Pattern - Azure Architecture Center | Microsoft Learn · 2026-05-28
- Strangler Fig Pattern - Azure Architecture Center | Microsoft Learn · 2026-05-29
- Strangler Fig (Martin Fowler bliki) · 2024-08-22
- Legacy system integration: when to wrap a system instead of replacing it · 2026-09-15
- RPA vs API Integration Explained: Which One Should You Use? · 2026-03-21
- Rebuilding Automation Anywhere Screen-Scraping Bots · 2026-09-21
- SAP Integration: RFC, OData, REST or IDoc? · 2026-06-09