Bạn đã bao giờ gõ “Data Engineering” vào Google và nhấp vào nút “I’m Feeling Lucky”, chỉ để thấy mình lạc vào một mê cung công cụ, thuật ngữ và khái niệm phức tạp? Nếu có, thì bài viết này chính là dành cho bạn.
Với vai trò là một kỹ sư phát triển web dày dặn kinh nghiệm, tôi đã quyết định dấn thân vào một lĩnh vực mới trong sự nghiệp của mình: Kỹ thuật Dữ liệu (Data Engineering). Đây là hành trình học hỏi và nghiên cứu của riêng tôi. Nếu bạn có bất kỳ đóng góp hay kiến thức nào muốn chia sẻ, đừng ngần ngại để lại bình luận bên dưới. Tôi rất mong muốn được học hỏi thêm!
Trong bài viết này, chúng ta sẽ cùng nhau khám phá những nguyên tắc cơ bản của Kỹ thuật Dữ liệu và tìm hiểu cách một công cụ mạnh mẽ tên là Bruin có thể đơn giản hóa toàn bộ quá trình, giúp bạn xây dựng các đường ống dữ liệu (data pipeline) phức tạp một cách dễ dàng.
- 1. Lời mở đầu
- 2. Ấn tượng đầu tiên: Khám phá cấu trúc của Bruin
- 3. Xây dựng Pipeline đầu tiên của chúng ta
- 4. Luồng dữ liệu tổng quan đầy đủ
- 5. Tiện ích mở rộng VS Code cực kỳ mạnh mẽ
- 6. Kết luận
Mục lục
1. Lời mở đầu
Tôi phải thừa nhận rằng, mỗi khi ai đó nhắc đến Kỹ thuật Dữ liệu (Data Engineering), tôi thường bỏ qua. Nó luôn có vẻ phức tạp đến mức không tưởng — gần như là phép thuật.
Tuần này, tôi cuối cùng đã quyết định dấn thân. Tôi nghĩ nó sẽ khá đơn giản, nhưng không mất nhiều thời gian để nhận ra mức độ sâu rộng của lĩnh vực này. Kỹ thuật dữ liệu không chỉ là một vài đoạn mã (script) hay câu lệnh SQL đơn thuần; nó là một hệ sinh thái toàn diện gồm các công cụ, khái niệm và trách nhiệm được kết nối với nhau, tạo thành xương sống của các hệ thống dữ liệu hiện đại.
Các khái niệm như:
- Danh mục và Quản trị Dữ liệu (Data Catalogs and Governance): Hiểu rõ ai sở hữu dữ liệu, làm thế nào để đảm bảo chất lượng và theo dõi nguồn gốc dữ liệu.
- Điều phối (Orchestration): Phối hợp các phụ thuộc và quy trình công việc với các công cụ như Apache Airflow hoặc Dagster.
- Chuyển đổi (Transformation – ETL/ELT): Làm sạch và chuẩn hóa dữ liệu bằng cách sử dụng các công cụ như dbt hoặc Fivetran.
- Thu thập và Truyền dữ liệu theo thời gian thực (Ingestion and Streaming): Kết nối các nguồn và di chuyển dữ liệu theo thời gian thực với Kafka, Airbyte hoặc Confluent Cloud.
- Khả năng quan sát và Chất lượng (Observability and Quality): Giám sát tình trạng dữ liệu với các giải pháp như Monte Carlo và Datafold.
Mỗi bài viết tôi đọc, mỗi công cụ tôi tìm hiểu lại mở ra một thế giới mới về các công cụ, thuật ngữ, framework, kiến trúc và các phương pháp tốt nhất. Và bằng cách nào đó, tất cả chúng phải hoạt động cùng nhau — quản trị, điều phối, chuyển đổi, thu thập, khả năng quan sát, cơ sở hạ tầng.
Là một nhà phát triển phần mềm, tôi đã quen với việc học một ngôn ngữ và một framework rồi bắt tay vào công việc. Nhưng trong kỹ thuật dữ liệu, mọi thứ lại khác. Đó là việc hiểu toàn bộ hệ sinh thái và cách mỗi thành phần kết nối với nhau.
Sau hàng giờ đọc tài liệu, theo dõi các kho GitHub, và nhảy giữa các công cụ, bài viết và vô số định nghĩa, cuối cùng tôi cũng tìm thấy công cụ đã làm cho mọi thứ trở nên rõ ràng — đó là Bruin.
Hãy tưởng tượng một framework duy nhất có thể:
- Pipelines as Code — Mọi thứ tồn tại trong văn bản được kiểm soát phiên bản (YAML, SQL, Python). Không có giao diện người dùng (UI) hoặc cơ sở dữ liệu ẩn. Khả năng tái tạo, xem xét và tự động hóa cao.
- Đa ngôn ngữ — Hỗ trợ gốc cho SQL và Python, cùng với khả năng tích hợp các binary hoặc container cho các trường hợp sử dụng phức tạp hơn.
- Các Pipeline có khả năng kết hợp — Kết hợp các công nghệ, nguồn và đích trong một luồng liền mạch — không cần mã kết nối (glue code), không cần các thủ thuật phức tạp.
- Không bị khóa nhà cung cấp (No Lock-In) — CLI mã nguồn mở 100% (được cấp phép Apache) chạy ở bất cứ đâu: cục bộ, trong CI, hoặc trong môi trường sản xuất. Bạn giữ toàn quyền kiểm soát các pipeline và dữ liệu của mình.
- Được xây dựng cho nhà phát triển và Chất lượng dữ liệu — Chạy cục bộ nhanh, kiểm tra tích hợp và phản hồi nhanh chóng. Các sản phẩm dữ liệu được kiểm thử, đáng tin cậy và dễ dàng triển khai.
… và nó phù hợp với tất cả các khái niệm cốt lõi của Kỹ thuật Dữ liệu mà tôi vừa đề cập trước đó.
Tôi phải thừa nhận — tôi là kiểu người ủng hộ _sự lười biếng hiệu quả_. Nếu có cách để làm được nhiều việc hơn với ít công cụ và ít ma sát hơn, tôi sẽ tham gia.
Vì vậy, trước khi chúng ta bắt đầu, đây là kế hoạch:
Trong hầu hết các thiết lập, dữ liệu chảy từ cơ sở dữ liệu OLTP → thu thập → hồ dữ liệu/kho dữ liệu → chuyển đổi → mart → bảng điều khiển phân tích.
Các công cụ như Airbyte xử lý việc thu thập, dbt xử lý chuyển đổi, Airflow điều phối các phụ thuộc — và Bruin kết hợp các lớp đó thành một framework thống nhất.
Bài viết này sẽ đi sâu vào các nguyên tắc cơ bản của Kỹ thuật Dữ liệu, đồng thời khám phá cách Bruin kết hợp tất cả chúng thông qua một pipeline đơn giản, thực tế.
2. Ấn tượng đầu tiên: Khám phá cấu trúc của Bruin
Thành thật mà nói — tôi từng có một chút thành kiến với các dự án Khoa học/Kỹ thuật Dữ liệu. Mỗi khi tôi nhìn vào một dự án, nó có vẻ lộn xộn và không có cấu trúc, với các tệp và sổ ghi chép rải rác khắp nơi. Là một người đến từ nền tảng phát triển phần mềm, loại hỗn loạn đó luôn làm tôi khó chịu.
Nhưng một khi tôi bắt đầu xem xét cấu trúc dự án của Bruin, nhận thức đó đã thay đổi hoàn toàn. Mọi thứ bỗng nhiên trở nên có tổ chức và có chủ đích. Framework này tự nhiên áp đặt cấu trúc thông qua các lớp của nó — và một khi bạn tuân theo chúng, mọi thứ bắt đầu có ý nghĩa.
Ví dụ: Cấu trúc dự án Bruin
├── duckdb.db
├── ecommerce-mart
│ ├── assets
│ │ ├── ingestion
│ │ │ ├── raw.customers.asset.yml
│ │ │ ├── raw.order_items.asset.yml
│ │ │ ├── raw.orders.asset.yml
│ │ │ ├── raw.products.asset.yml
│ │ │ └── raw.product_variants.asset.yml
│ │ ├── mart
│ │ │ ├── mart.customers-by-age.asset.py
│ │ │ ├── mart.customers-by-country.asset.yml
│ │ │ ├── mart.product_performance.sql
│ │ │ ├── mart.sales_daily.sql
│ │ │ └── mart.variant_profitability.sql
│ │ └── staging
│ │ ├── stg.customers.asset.yml
│ │ ├── stg.order_items.sql
│ │ ├── stg.orders.sql
│ │ ├── stg.products.sql
│ │ └── stg.product_variants.sql
│ └── pipeline.yml
├── glossary.yml
├── policy.yml
└── .bruin.yml
Ý nghĩa của từng thành phần
.bruin.yml
- Tệp cấu hình chính cho môi trường Bruin của bạn.
- Định nghĩa cài đặt toàn cục như kết nối mặc định, biến và hành vi cho tất cả các pipeline.
policy.yml
- Tệp chính sách quản trị và xác thực dữ liệu của bạn.
- Định nghĩa các quy tắc chất lượng dữ liệu, kiểm soát truy cập và kiểm tra tuân thủ mà Bruin có thể tự động thực thi trước khi triển khai các sản phẩm dữ liệu.
glossary.yml
- Hoạt động như một danh mục dữ liệu nhẹ (lightweight data catalog) cho dự án của bạn.
- Tài liệu hóa các thuật ngữ, số liệu và tập dữ liệu để mọi người trong nhóm đều nói cùng một ngôn ngữ.
- Cũng giúp ích cho việc theo dõi nguồn gốc dữ liệu (lineage), tài liệu và khả năng khám phá.
some-feature/pipeline.yml
- Định nghĩa một pipeline cụ thể cho một lĩnh vực hoặc dự án (trong ví dụ này, _ecommerce_).
- Mô tả luồng dữ liệu đầu cuối — những asset nào sẽ chạy, các phụ thuộc của chúng và lịch trình.
- Các pipeline có tính mô-đun, cho phép bạn duy trì các pipeline riêng biệt cho các lĩnh vực kinh doanh khác nhau.
some-feature/assets/*
- Chứa tất cả các asset — các khối xây dựng của đường ống dữ liệu của bạn.
- Mỗi asset xử lý một tác vụ riêng biệt: thu thập dữ liệu thô, chuyển đổi dữ liệu hoặc tạo bảng phân tích.
- Vì mỗi asset là một tệp, nó được kiểm soát phiên bản, có thể kiểm thử và tái sử dụng — giống như mã nguồn.
Chỉ với những thành phần đó, chúng ta đã có thể chạy một pipeline hoàn chỉnh. Tuy nhiên, tôi vẫn nghĩ chúng ta cần đi qua từng bước và từng tệp một cách riêng lẻ — tôi hứa sẽ nhanh thôi!
2.1. Tệp cấu hình chính: .bruin.yml
Hãy coi .bruin.yml
như là cấu hình gốc của dự án bạn — tệp này cho Bruin biết _cách thức_ và _nơi_ để chạy mọi thứ.
Thay vì rải rác cài đặt trên các script hoặc biến môi trường, Bruin tập trung chúng tại đây: các kết nối, thông tin xác thực và cấu hình dành riêng cho môi trường đều nằm ở một nơi. Nó cũng đóng vai trò là backend bí mật mặc định của Bruin, để các pipeline của bạn có thể truy cập các cơ sở dữ liệu hoặc kho dữ liệu một cách an toàn và nhất quán.
Để chạy pipeline với tệp cấu hình cụ thể:
bruin run ecommerce/pipeline.yml --config-file /path/to/.bruin.yml
Một ví dụ đơn giản:
default_environment: default
environments:
default:
connections:
postgres:
- name: pg-default
username: postgres # (hardcoded as well)
password: ${PG_PASSWORD}
host: ${PG_HOST}
port: ${PG_PORT}
database: ${PG_DATABASE}
duckdb:
- name: duckdb-default
path: duckdb.db
Điều gì đang xảy ra ở đây?
default_environment
— đặt môi trường mà Bruin sẽ sử dụng nếu không được chỉ định khác.environments
— định nghĩa nhiều thiết lập (ví dụ: _dev_, _staging_, _prod_), mỗi thiết lập có cấu hình riêng.connections
— liệt kê mọi hệ thống mà Bruin có thể kết nối, như Postgres hoặc DuckDB. Mỗi kết nối có một tên (ví dụ:pg-default
) mà bạn sẽ tham chiếu trong các pipeline và asset.- Hỗ trợ biến môi trường — bất kỳ giá trị nào được bao quanh bởi
${...}
sẽ tự động được đọc từ môi trường hệ thống của bạn.
Điều này có nghĩa là bạn có thể giữ thông tin xác thực bên ngoài hệ thống kiểm soát phiên bản (source control) trong khi vẫn chạy cục bộ hoặc trong môi trường CI/CD.
Thiết kế này giữ mọi thứ tập trung, an toàn và được kiểm soát phiên bản, đồng thời cung cấp cho bạn sự linh hoạt để đưa các khóa bí mật (secrets) một cách linh hoạt thông qua các biến môi trường — hoàn hảo để chuyển đổi giữa môi trường cục bộ, staging và sản xuất mà không cần chỉnh sửa mã nguồn.
2.2. Pipeline: Một Apache Airflow dễ dàng hơn nhiều
Đối với mỗi tính năng bạn có, nó sẽ đi kèm với một tệp pipeline.yml
.
Đây là tệp sẽ nhóm tất cả các asset của bạn và hiểu rằng đó không phải là một asset đơn lẻ đang chạy, mà là một danh sách các asset được xâu chuỗi.
- ecommerce-mart/
├─ pipeline.yml -> bạn đang ở đây
└─ assets/
├─ some-asset.sql
├─ definitely-an-asset.yml
└─ another-asset.py
Tại đó, bạn cũng cấu hình mỗi kết nối bạn muốn sử dụng trên pipeline cụ thể:
name: product_ecommerce_marts
schedule: daily # relevant for Bruin Cloud deployments
default_connections:
duckdb: "duckdb-default"
postgres: "pg-default"
2.3. Assets: Các khối xây dựng sản phẩm dữ liệu
Mỗi đường ống dữ liệu trong Bruin được cấu thành từ assets — các đơn vị mô-đun, độc lập, định nghĩa một hoạt động cụ thể: thu thập, chuyển đổi hoặc tạo một tập dữ liệu.
Mỗi asset tồn tại dưới dạng một tệp trong thư mục assets/
, và tên tệp của nó đồng thời là định danh của nó trong biểu đồ pipeline.
Nếu bạn nhớ cấu trúc tệp được hiển thị ở phần đầu, bạn phải nhớ rằng tôi có nhiều loại asset trong pipeline. Đó là phần thú vị nhất, vì bạn có thể viết bằng nhiều ngôn ngữ khác nhau mà vẫn giữ được sự đơn giản. Dưới đây là một số khả năng:
Loại | Mô tả | Tên tệp (trong cây thư mục) |
---|---|---|
YAML | Cấu hình khai báo cho việc thu thập hoặc các asset nặng về metadata | raw.customers.asset.yml |
SQL | Logic chuyển đổi thuần túy — giống như các mô hình kiểu dbt | stg.orders.sql |
Python | Logic tùy chỉnh hoặc tích hợp (ví dụ: API, xác thực, hoặc các bước học máy) | mart.sales_daily.asset.py |
Bạn có thể tổ chức các asset theo cách bạn muốn — không có hệ thống phân cấp cứng nhắc nào cần tuân theo.
Điểm mấu chốt là việc điều phối xảy ra một cách ngầm định thông qua các phụ thuộc, chứ không phải thông qua một công cụ DAG (Directed Acyclic Graph) bên ngoài như Airflow.
Mỗi asset khai báo những gì nó phụ thuộc, và Bruin tự động xây dựng và thực thi biểu đồ phụ thuộc cho bạn.
Ví dụ:
raw.orders.asset.yml
# raw.orders.asset.yml
name: raw.orders
type: ingestr
description: Ingest OLTP orders from Postgres into the DuckDB raw layer.
parameters:
source_connection: pg-default
source_table: "public.orders"
destination: duckdb
raw.order_items.asset.yml
# raw.order_items.asset.yml
name: raw.order_items
type: ingestr
description: Ingest OLTP order_items from Postgres into the DuckDB raw layer.
depends:
- raw.orders # khai báo phụ thuộc vào asset 'raw.orders'
parameters:
source_connection: pg-default
source_table: "public.order_items"
destination: duckdb
…biến thành biểu đồ phụ thuộc:
graph TD
raw.orders --> raw.order_items;
Bằng cách xâu chuỗi các asset như vậy, bạn mô tả các mối quan hệ logic giữa các hoạt động dữ liệu thay vì điều phối các bước một cách thủ công.
Kết quả là một pipeline khai báo, có khả năng kết hợp và dễ bảo trì — dễ đọc, kiểm soát phiên bản và mở rộng giống như mã ứng dụng.
Một trong những khía cạnh mạnh mẽ nhất của Bruin là cách nó kết nối chất lượng dữ liệu và quản trị dữ liệu trực tiếp vào các asset của bạn.
Bằng cách định nghĩa các kiểm tra dưới mỗi cột, bạn không chỉ xác thực dữ liệu của mình mà còn tài liệu hóa quyền sở hữu, kỳ vọng và các ràng buộc — tất cả đều được kiểm soát phiên bản và có thể thực thi trong thời gian chạy.
Điều này có nghĩa là Bruin không chỉ _chạy_ các pipeline — nó còn kiểm toán, tài liệu hóa và quản trị chúng như một phần của cùng một quy trình làm việc.
2.4. Policies: Thực thi chất lượng và quản trị
Các chính sách (Policies) trong Bruin hoạt động như một bộ quy tắc giúp các đường ống dữ liệu của bạn nhất quán, tuân thủ và chất lượng cao.
Chúng đảm bảo mọi asset và pipeline tuân theo các phương pháp tốt nhất — từ quy ước đặt tên và quyền sở hữu đến xác thực và hoàn chỉnh siêu dữ liệu.
Về cơ bản, các chính sách được định nghĩa trong một tệp policy.yml
duy nhất nằm ở thư mục gốc của dự án bạn.
Tệp này cho phép bạn kiểm tra cú pháp (lint), xác thực và thực thi các tiêu chuẩn tự động trước khi pipeline chạy.
Tổng quan nhanh
rulesets:
- name: standard
selector:
- path: .*/ecommerce/.*
rules:
- asset-has-owner
- asset-name-is-lowercase
- asset-has-description
Mỗi ruleset định nghĩa:
- nơi quy tắc áp dụng (
selector
→ khớp theo đường dẫn, thẻ hoặc tên), - những gì cần thực thi (
rules
→ các quy tắc xác thực tích hợp sẵn hoặc tùy chỉnh).
Sau khi định nghĩa, bạn có thể xác thực toàn bộ dự án của mình:
bruin validate ecommerce
# Validating pipelines in 'ecommerce' for 'default' environment...
# Pipeline: ecommerce_pg_to_duckdb (.)
# raw.order_items (assets/ingestion/raw.order_items.asset.yml)
# └── Asset must have an owner (policy:standard:asset-has-owner)
Bruin tự động kiểm tra cú pháp (lìnt) các asset trước khi thực thi — đảm bảo rằng các pipeline không tuân thủ sẽ không bao giờ chạy.
Các quy tắc tích hợp sẵn và tùy chỉnh
Quy tắc | Đối tượng | Mô tả |
---|---|---|
asset-has-owner |
asset | Mỗi asset phải định nghĩa một chủ sở hữu. |
asset-has-description |
asset | Assets phải bao gồm một mô tả. |
asset-name-is-lowercase |
asset | Tên asset phải là chữ thường. |
pipeline-has-retries |
pipeline | Pipelines phải định nghĩa cài đặt thử lại. |
Bạn cũng có thể định nghĩa các quy tắc của riêng mình:
custom_rules:
- name: asset-has-owner
description: every asset should have an owner
criteria: asset.Owner != ""
Các quy tắc có thể nhắm mục tiêu vào cả assets hoặc pipelines, và chúng sử dụng các biểu thức logic để xác định sự tuân thủ.
Các chính sách biến Bruin thành một nền tảng dữ liệu tự quản trị — nơi mà các phương pháp tốt nhất không phải là tùy chọn, mà được thực thi.
Bằng cách cam kết các quy tắc của bạn vào kiểm soát phiên bản, bạn biến quản trị dữ liệu thành một phần của quy trình phát triển, chứ không phải là một điều sau cùng.
2.5. Glossary: Nói cùng một ngôn ngữ
Trong các dự án dữ liệu, một trong những vấn đề khó khăn nhất không phải là kỹ thuật — mà là giao tiếp.
Các nhóm khác nhau thường sử dụng cùng một từ để chỉ những thứ khác nhau. Đó là nơi mà Glossary của Bruin phát huy tác dụng.
Một danh mục thuật ngữ (glossary) được định nghĩa trong glossary.yml
ở thư mục gốc của dự án bạn.
Nó hoạt động như một từ điển chia sẻ các khái niệm kinh doanh (như _Khách hàng_ hoặc _Đơn hàng_) và các thuộc tính của chúng, giúp các nhóm luôn đồng bộ hóa trong các pipeline.
entities:
Customer:
description: A registered user or business in our platform.
attributes:
ID:
type: integer
description: Unique customer identifier.
Bạn có thể tham chiếu các định nghĩa này bên trong các asset bằng cách sử dụng extends
, tránh trùng lặp và đảm bảo tính nhất quán:
# raw.customers.asset.yml
name: raw.customers
type: ingestr
columns:
- name: customer_id
extends: Customer.ID
Điều này tự động kế thừa type
và description
từ danh mục thuật ngữ.
Đây là một ý tưởng đơn giản nhưng mạnh mẽ — các định nghĩa dữ liệu của bạn trở nên được kiểm soát phiên bản và chia sẻ, giống như mã nguồn.
3. Xây dựng Pipeline đầu tiên của chúng ta
Bây giờ chúng ta đã khám phá cấu trúc và triết lý đằng sau Bruin, đã đến lúc xây dựng một pipeline từ đầu đến cuối.
Chúng ta sẽ đi từ thu thập dữ liệu thô (raw ingestion) đến một lớp staging sạch, và cuối cùng, đến các mart sẵn sàng cho phân tích — tất cả đều được định nghĩa dưới dạng mã.
Chúng ta sẽ giả định bạn đã có sẵn:
- Một cơ sở dữ liệu Postgres làm nguồn dữ liệu của bạn.
- Một cơ sở dữ liệu DuckDB làm nơi lưu trữ phân tích của bạn.
- Một tệp
.bruin.yml
hoạt động đã được cấu hình với cả hai kết nối này.
3.1. Bước 1: Thu thập dữ liệu từ nguồn vào Data Lake
Bước đầu tiên là di chuyển dữ liệu từ Postgres vào DuckDB.
Điều này tạo ra Lớp Dữ liệu Thô (Raw Layer) của bạn — dữ liệu được sao chép từ nguồn với sự biến đổi tối thiểu.
Tạo một tệp asset thu thập dữ liệu:
touch assets/ingestion/raw.customers.asset.yml
Sau đó định nghĩa asset:
# assets/ingestion/raw.customers.asset.yml
name: raw.customers
type: ingestr
description: Ingest OLTP customers from Postgres into the DuckDB raw layer.
parameters:
source_connection: pg-default
source_table: "public.customers"
destination: duckdb
columns:
- name: id
type: integer
primary_key: true
checks:
- name: not_null
- name: unique
- name: email
type: string
checks:
- name: not_null
- name: unique
- name: country
type: string
checks:
- name: not_null
Điều này yêu cầu Bruin trích xuất dữ liệu từ bảng public.customers
trong Postgres của bạn, xác thực chất lượng cột và lưu trữ nó trong lớp dữ liệu thô của DuckDB.
Chạy Asset
bruin run ecommerce/assets/ingestion/raw.customers.asset.yml
Kết quả mong đợi:
Analyzed the pipeline 'ecommerce_pg_to_duckdb' with 13 assets.
Running only the asset 'raw.customers'
Pipeline: ecommerce_pg_to_duckdb (../../..)
No issues found
✓ Successfully validated 13 assets across 1 pipeline, all good.
Interval: 2025-10-12T00:00:00Z - 2025-10-12T23:59:59Z
Starting the pipeline execution...
PASS raw.customers ........
bruin run completed successfully in 2.095s
✓ Assets executed 1 succeeded
Bây giờ bạn có thể truy vấn dữ liệu đã được thu thập:
bruin query --connection duckdb-default --query "SELECT * FROM raw.customers LIMIT 5"
Kết quả:
┌────┬───────────────────┬───────────────────────────┬───────────┬──────────────────┬──────────────────────────────────────┬──────────────────────────────────────┐
│ ID │ FULL_NAME │ EMAIL │ COUNTRY │ CITY │ CREATED_AT │ UPDATED_AT │
├────┼───────────────────┼───────────────────────────┼───────────┼──────────────────┼──────────────────────────────────────┼──────────────────────────────────────┤
│ 1 │ Allison Hill │ donaldgarcia@example.net │ Uganda │ New Roberttown │ 2025-10-10 18:19:13.083281 +0000 UTC │ 2025-10-10 00:42:59.71112 +0000 UTC │
│ 2 │ David Guzman │ jennifermiles@example.com │ Cyprus │ Lawrencetown │ 2025-10-10 07:52:47.643619 +0000 UTC │ 2025-10-10 06:23:42.864287 +0000 UTC │
│ 3 │ Caitlin Henderson │ eric51@example.org │ Hong Kong │ West Melanieview │ 2025-10-10 21:06:02.639412 +0000 UTC │ 2025-10-10 19:23:17.540169 +0000 UTC │
│ 4 │ Monica Herrera │ smiller@example.net │ Niger │ Barbaraland │ 2025-10-11 01:33:43.032929 +0000 UTC │ 2025-10-10 02:29:27.22515 +0000 UTC │
│ 5 │ Darren Roberts │ wyattmichelle@example.com │ Fiji │ Reidstad │ 2025-10-10 12:05:18.734246 +0000 UTC │ 2025-10-10 00:51:13.406526 +0000 UTC │
└────┴───────────────────┴───────────────────────────┴───────────┴──────────────────┴──────────────────────────────────────┴──────────────────────────────────────┘
Lớp dữ liệu thô (raw layer) của bạn hiện đã được thiết lập và xác thực.
3.2. Bước 2: Định dạng và xác thực dữ liệu (Lớp Staging)
Tiếp theo, chúng ta sẽ làm sạch và chuẩn hóa dữ liệu đã được thu thập trước khi sử dụng nó trong phân tích.
Lớp này được gọi là Staging (stg) — đây là nơi bạn thực thi lược đồ (schema), tính nhất quán của cột và áp dụng các quy tắc kinh doanh.
Tạo tệp:
touch ecommerce/assets/staging/stg.customers.asset.sql
Và định nghĩa nó như sau:
/* @bruin
name: stg.customers
type: duckdb.sql
materialization:
type: table
depends:
- raw.customers
checks:
columns:
id:
- not_null
email:
- not_null
- unique
country:
- not_null
@bruin */
SELECT id::INT AS customer_id,
COALESCE(TRIM(email), '') AS email,
COALESCE(TRIM(country), 'Unknown') AS country,
created_at,
updated_at
FROM raw.customers
WHERE email IS NOT NULL;
Dưới đây là những gì đang xảy ra:
- Khối chú thích Bruin (
@bruin
) định nghĩa siêu dữ liệu cho asset. - Khóa
depends
đảm bảo bước staging này chỉ chạy sau khiraw.customers
hoàn tất — Bruin tự động quản lý chuỗi phụ thuộc. - Các kiểm tra cột đảm bảo chất lượng dữ liệu trước và sau khi chuyển đổi.
- Truy vấn SQL tự nó thực hiện việc làm sạch nhẹ và thực thi tính nhất quán về kiểu dữ liệu.
Thiết kế này mô phỏng các công cụ điều phối như Airflow, nhưng không cần bộ lập lịch bên ngoài — các phụ thuộc và kiểm tra được khai báo ngay trong mã của bạn.
Xác thực và Thực thi
Trước khi chạy, hãy xác thực asset:
bruin validate ecommerce/assets/staging/stg.customers.asset.sql
Kết quả mong đợi:
Pipeline: ecommerce_pg_to_duckdb (.)
No issues found
✓ Successfully validated 13 assets across 1 pipeline, all good.
Bây giờ thực thi nó:
bruin run ecommerce/assets/staging/stg.customers.asset.sql
Kết quả:
bruin run ecommerce/assets/staging/stg.customers.asset.sql
Analyzed the pipeline 'ecommerce_pg_to_duckdb' with 15 assets.
Running only the asset 'stg.customers'
Pipeline: ecommerce_pg_to_duckdb (../../..)
No issues found
✓ Successfully validated 15 assets across 1 pipeline, all good.
Interval: 2025-10-12T00:00:00Z - 2025-10-12T23:59:59Z
Starting the pipeline execution...
[21:28:16] Running: stg.customers
[21:28:16] Finished: stg.customers (41ms)
==================================================
PASS stg.customers
bruin run completed successfully in 41ms
✓ Assets executed 1 succeeded
Xác nhận rằng quá trình chuyển đổi đã hoạt động:
bruin query "SELECT country, COUNT(*) AS customers FROM stg.customers GROUP BY country ORDER BY customers DESC;"
Ví dụ kết quả:
country | customers
--------------+-----------
BRAZIL | 420
GERMANY | 255
UNITED STATES | 198
ARGENTINA | 190
SOUTH KOREA | 182
Tại thời điểm này, bạn có một tập dữ liệu sạch, đã được xác thực sẵn sàng cho phân tích.
3.3. Bước 3: Thiết kế lớp phân tích (Mart)
Bước cuối cùng là xây dựng Lớp Mart của bạn, nơi chứa dữ liệu sẵn sàng cho doanh nghiệp.
Đây là lớp mà các nhà phân tích và bảng điều khiển truy vấn trực tiếp.
Mỗi asset Mart tổng hợp hoặc định hình lại dữ liệu staging thành các tập dữ liệu có ý nghĩa cho việc báo cáo và phân tích.
3.3.1. Asset: mart.customers_by_country.asset.sql
Tạo tệp sau:
touch ecommerce/assets/mart/mart.customers_by_country.asset.sql
Sau đó định nghĩa asset:
/* @bruin
name: mart.customers_by_country
type: duckdb.sql
materialization:
type: table
depends:
- stg.customers
@bruin */
SELECT
country,
COUNT(*) AS total_customers
FROM stg.customers
GROUP BY country
ORDER BY total_customers DESC;
Asset SQL này tổng hợp khách hàng theo quốc gia và phụ thuộc vào stg.customers
, đảm bảo lớp staging chạy trước. Nó được hiện thực hóa thành một bảng trong DuckDB.
Chạy nó:
bruin run ecommerce/assets/mart/mart.customers_by_country.asset.sql
Kết quả mong đợi:
Pipeline: ecommerce_pg_to_duckdb (.)
Running mart.customers_by_country
✓ Table materialized successfully in DuckDB
Xác minh kết quả:
bruin query --connection duckdb-default --query "SELECT * FROM mart.customers_by_country LIMIT 5;"
Kết quả:
┌───────────────┬───────────────────┐
│ COUNTRY │ TOTAL_CUSTOMERS │
├───────────────┼───────────────────┤
│ BRAZIL │ 420 │
│ GERMANY │ 255 │
│ UNITED STATES │ 198 │
│ ARGENTINA │ 190 │
│ SOUTH KOREA │ 182 │
└───────────────┴───────────────────┘
3.3.2. Asset: mart.customers_by_age.asset.sql
Bây giờ, hãy tạo một mart thứ hai phân đoạn khách hàng thành các nhóm tuổi.
Tạo tệp:
touch ecommerce/assets/mart/mart.customers_by_age.asset.sql
Định nghĩa nó như sau:
/* @bruin
name: mart.customers_by_age
type: duckdb.sql
materialization:
type: table
depends:
- stg.customers
@bruin */
WITH src AS (
SELECT
CASE
WHEN age < 25 THEN '18-24'
WHEN age BETWEEN 25 AND 34 THEN '25-34'
WHEN age BETWEEN 35 AND 49 THEN '35-49'
ELSE '50+'
END AS age_group
FROM stg.customers
)
SELECT
age_group,
COUNT(*) AS total_customers
FROM src
GROUP BY age_group
ORDER BY total_customers DESC;
Asset này tính toán phân bố khách hàng theo nhóm tuổi bằng cách sử dụng biểu thức CASE
đơn giản và tổng hợp kết quả.
Chạy nó:
bruin run ecommerce/assets/mart/mart.customers_by_age.asset.sql
Kết quả mong đợi:
bruin run ecommerce/assets/mart/mart.customers_by_age.asset.sql
Analyzed the pipeline 'ecommerce_pg_to_duckdb' with 15 assets.
Running only the asset 'mart.customers_by_age'
Pipeline: ecommerce_pg_to_duckdb (../../..)
No issues found
✓ Successfully validated 15 assets across 1 pipeline, all good.
Interval: 2025-10-12T00:00:00Z - 2025-10-12T23:59:59Z
Starting the pipeline execution...
[21:10:24] Running: mart.customers_by_age
[21:10:24] Finished: mart.customers_by_age (39ms)
==================================================
PASS mart.customers_by_age
bruin run completed successfully in 39ms
✓ Assets executed 1 succeeded
Xác nhận rằng mart đã được điền đúng cách:
bruin query --connection duckdb-default --query "SELECT * FROM mart.customers_by_age;"
Kết quả:
┌─────────────┬───────────────────┐
│ AGE_GROUP │ TOTAL_CUSTOMERS │
├─────────────┼───────────────────┤
│ 25-34 │ 460 │
│ 35-49 │ 310 │
│ 18-24 │ 250 │
│ 50+ │ 225 │
└─────────────┴───────────────────┘
4. Luồng dữ liệu tổng quan đầy đủ
Hy vọng rằng, tôi đã truyền tải đúng, các khái niệm được giải thích ở phần đầu không quá phức tạp và chúng ta thực sự đã biến pipeline thành hiện thực chỉ bằng Bruin và một vài truy vấn!
Pipeline ví dụ của chúng ta trông như thế này:
graph TD
raw.customers --> stg.customers
stg.customers --> mart.customers_by_country
stg.customers --> mart.customers_by_age
Ở giai đoạn này, bạn đã xây dựng một đường ống dữ liệu khai báo, từ đầu đến cuối với Bruin — thu thập, staging và phân tích — tất cả đều được quản trị, xác thực và có thể tái tạo, không cần lớp điều phối bên ngoài nào.
5. Tiện ích mở rộng VS Code cực kỳ mạnh mẽ
Khi tôi lần đầu tiên cài đặt Tiện ích mở rộng Bruin cho VS Code, tôi không biết mình đang làm gì. Lúc đó, tôi thực sự không hiểu Bruin — hay thậm chí cả Kỹ thuật Dữ liệu — hoạt động như thế nào. Tôi bấm lung tung, thấy một đống tệp YAML và các điểm đánh dấu metadata, rồi nhanh chóng bỏ cuộc.
Một tuần sau, sau khi cuối cùng đã hiểu được hệ sinh thái — thu thập, staging, marts và quản trị — tôi quyết định mở lại tiện ích mở rộng, và đó là lúc mọi thứ trở nên rõ ràng.
Nó không chỉ là một công cụ hỗ trợ. Đó là mảnh ghép còn thiếu.
Tiện ích mở rộng này mang sức mạnh khai báo tương tự của CLI Bruin vào một môi trường trực quan, thân thiện với nhà phát triển. Nó tự động quét các asset của bạn, xác thực cấu hình, chạy truy vấn trực tiếp vào cơ sở dữ liệu của bạn, và thậm chí quản lý các tệp YAML của bạn theo thời gian thực.
Mọi thứ diễn ra bên trong VS Code — xác thực, khám phá nguồn gốc dữ liệu (lineage), kiểm tra siêu dữ liệu và xem trước truy vấn.
Điều làm tôi ấn tượng nhất là cách nó linh hoạt và mở. Không có sự khóa nhà cung cấp (vendor lock-in) ở đây — nó hoàn toàn mã nguồn mở.
Bạn có thể fork nó, mở rộng nó hoặc đóng góp ngược lại cho cộng đồng giống như chính CLI.
Tóm lại, Tiện ích mở rộng Bruin cho VS Code không chỉ là một công cụ đồng hành — đó là sự phát triển tự nhiên của quy trình làm việc. Một khi bạn hiểu Bruin, công cụ này sẽ mang lại cảm giác như phép thuật cuối cùng đã được giải thích.
6. Kết luận
Nghiên cứu này là nỗ lực thực sự đầu tiên của tôi để hiểu Bruin và ý nghĩa thực tế của Kỹ thuật Dữ liệu. Điều từng cảm thấy trừu tượng và phức tạp bắt đầu có ý nghĩa hơn một chút sau khi chia nhỏ mọi thứ, thử nghiệm và kết nối các điểm từng bước một.
Tôi sẽ không nói rằng tôi đã hiểu hoàn toàn mọi thứ — còn lâu mới đạt được điều đó — nhưng cuối cùng tôi đã có thể thấy các mảnh ghép phù hợp với nhau như thế nào: thu thập, staging, marts, xác thực và quản trị. Bruin đã giúp tôi tiếp cận các khái niệm đó một cách thực tế mà không cảm thấy quá sức.
Quá trình khám phá, thất bại, đọc và xây dựng lại hóa ra là phần có giá trị nhất. Vẫn còn rất nhiều điều để học, nhưng đây là một bước khởi đầu vững chắc để thực sự hiểu cách dữ liệu di chuyển, biến đổi và kể một câu chuyện (giống như bài viết này).