概念定义
数据契约是生产方向消费方承诺的带版本规范:字段名与类型、允许取值、可空性、新鲜度与数据量范围,以及归属人。它在 CI 与边界处强制执行,使破坏性变更无法悄悄抵达消费方。模式只描述结构,契约还约束行为——数据迟到如何处理、删除记录怎样处理、检查失败时呼叫谁。
为什么重要
在模块化的数据平台里,没人知道某张表的全部下游用途。一次悄悄的改名、一个新引入的空值或一次时区偏移,可能同时打断看板、模型与合规报表。契约把这些突发状况变成经过评审、公告与版本化的变更,这正是“事故”与“日常发布”的区别。
如何落地
从被消费最多、风险最高的数据集开始,把消费方早已假设的约定写下来。在生产流水线的每次构建中校验它,发布生成的文档,并把违规路由给生产方的值班人。破坏性变更给一个新的主版本并留出迁移窗口。久而久之,“数据如何表现”的部落知识就变成了无需人工即可运行的检查。
代码示例
<table>
<caption>
Employee Directory - Q1 2025
</caption>
<colgroup>
<col class="name-col">
<col class="dept-col">
<col class="role-col">
<col class="salary-col">
</colgroup>
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Department</th>
<th scope="col">Role</th>
<th scope="col" class="numeric">Salary</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Alice Johnson</th>
<td>Engineering</td>
<td>Senior Developer</td>
<td class="numeric">$95,000</td>
</tr>
<tr>
<th scope="row">Bob Smith</th>
<td>Marketing</td>
<td>Manager</td>
<td class="numeric">$85,000</td>
</tr>
<tr>
<th scope="row">Carol Davis</th>
<td>Engineering</td>
<td>Team Lead</td>
<td class="numeric">$110,000</td>
</tr>
</tbody>
<tfoot>
<tr>
<th scope="row" colspan="3">Average Salary</th>
<td class="numeric">$96,667</td>
</tr>
</tfoot>
</table>
<style>
table { border-collapse: collapse; width: 100%; }
th, td { padding: 0.75rem; text-align: left; border-bottom: 1px solid #ddd; }
.numeric { text-align: right; }
caption { font-weight: bold; margin-bottom: 0.5rem; }
</style>
import java.util.List;
import java.util.Objects;
public class RecordClasses {
public record Point(int x, int y) {
public Point {
if (x < 0 || y < 0) {
throw new IllegalArgumentException(
"Coordinates must be non-negative");
}
}
public double distanceTo(Point other) {
return Math.sqrt(
Math.pow(x - other.x, 2) +
Math.pow(y - other.y, 2)
);
}
}
public record Rectangle(Point topLeft, Point bottomRight) {
public int width() {
return bottomRight.x() - topLeft.x();
}
public int height() {
return bottomRight.y() - topLeft.y();
}
public int area() {
return width() * height();
}
}
public record Employee(
String name,
String department,
double salary,
List<String> skills
) {
public Employee {
Objects.requireNonNull(name);
skills = List.copyOf(skills);
}
}
public static void main(String[] args) {
Point p1 = new Point(0, 0);
Point p2 = new Point(3, 4);
System.out.println("Distance: " + p1.distanceTo(p2));
Rectangle rect = new Rectangle(p1, p2);
System.out.println("Area: " + rect.area());
Employee emp = new Employee("Alice", "Engineering", 95000,
List.of("Java", "Python", "SQL"));
System.out.println(emp);
}
}