首页
/ Vogen:打造类型安全的.NET领域模型

Vogen:打造类型安全的.NET领域模型

2026-03-11 06:03:06作者:翟萌耘Ralph

在软件开发中,基础类型如intstring常常被过度使用,导致"原始类型痴迷症"——当int既代表用户ID、订单号又代表数量时,编译器无法帮助我们发现逻辑错误。Vogen作为.NET生态中的价值对象生成工具,通过源代码生成技术将基础类型转化为具有业务含义的强类型,从编译阶段就杜绝此类错误。

Vogen项目Logo

定位价值对象:从原始类型到业务实体

价值对象(Value Object)是领域驱动设计中的核心概念,可理解为"业务领域的专用数据类型"。它将基础类型与业务规则绑定,例如CustomerId不仅是int类型,还包含"必须大于0"的验证规则。Vogen通过以下特性解决原始类型滥用问题:

消除类型歧义

当方法参数同时接收int userIdint orderId时,调用者可能因参数顺序错误导致生产事故。使用Vogen生成的UserIdOrderId类型,编译器会自动检查类型匹配,彻底避免此类错误。

内置业务规则

价值对象可封装验证逻辑,确保数据始终符合业务规则。例如Email类型会自动验证格式,PositiveAmount确保数值为正,杜绝无效数据进入系统。

提升代码可读性

string name替换为CustomerName name后,代码自文档化能力显著提升。新团队成员无需查看注释即可理解变量的业务含义。

常见问题

Q: 价值对象与普通类有何区别?
A: 价值对象注重值相等性而非引用相等性,且不可变。Vogen生成的类型自动实现值相等比较,两个具有相同基础值的CustomerId会被视为相等。

Q: 使用价值对象会影响性能吗?
A: 不会。Vogen在编译时生成代码,运行时性能与原始类型接近,且避免了运行时类型检查开销。

Q: 哪些场景适合使用价值对象?
A: 任何具有业务规则的基础类型都适合,如ID、金额、邮箱、电话等。尤其推荐在领域模型和API契约中使用。

快速集成Vogen:从安装到第一个价值对象

Vogen采用NuGet包分发,集成过程仅需三步,如同为项目安装一个普通依赖库。

获取项目代码

🔧 克隆Vogen项目仓库到本地开发环境:

git clone https://gitcode.com/gh_mirrors/vo/Vogen

添加NuGet包引用

🔧 在目标项目中安装Vogen包:

dotnet add package Vogen

⚠️ 建议指定稳定版本号,如dotnet add package Vogen --version 3.0.0

定义第一个价值对象

🔧 创建CustomerId.cs文件,添加以下代码:

using Vogen;

[ValueObject(typeof(int))]
public partial struct CustomerId { }

编译项目后,Vogen会自动生成完整的价值对象实现,包含验证、转换和相等性比较等功能。

代码对比:传统方式vs Vogen方式

传统原始类型实现 Vogen价值对象实现
public int CustomerId { get; set; } public CustomerId CustomerId { get; set; }
需手动验证CustomerId > 0 自动验证基础值有效性
允许orderId = customerId赋值错误 编译时阻止不同类型间赋值
需额外代码实现相等比较 自动实现值相等比较

常见问题

Q: 如何为价值对象添加自定义验证规则?
A: 添加静态Validate方法:

[ValueObject(typeof(int))]
public partial struct CustomerId 
{
    private static Validation Validate(int value)
    {
        if (value <= 0)
            return Validation.Invalid("ID必须大于0");
        return Validation.Valid;
    }
}

Q: 生成的代码存放位置在哪里?
A: 在项目的obj/Debug/netX.X/generated/Vogen目录下,可查看生成的完整代码。

Q: 如何在JSON序列化中使用价值对象?
A: Vogen自动生成System.Text.Json转换器,无需额外配置即可序列化/反序列化。

深度应用Vogen:定制化与高级场景

Vogen不仅支持基础价值对象创建,还提供丰富的定制选项,满足复杂业务需求。通过属性配置和部分类扩展,可实现从简单到复杂的各类价值类型。

构建带格式约束的字符串类型

以下示例创建只能包含字母和数字的Username类型:

[ValueObject(typeof(string))]
[Instance(InstanceGeneration.None)]
public partial class Username
{
    private static Validation Validate(string value)
    {
        if (string.IsNullOrWhiteSpace(value))
            return Validation.Invalid("用户名不能为空");
            
        if (!Regex.IsMatch(value, @"^[a-zA-Z0-9]+$"))
            return Validation.Invalid("用户名只能包含字母和数字");
            
        return Validation.Valid;
    }
    
    public static Username FromString(string value)
    {
        var result = TryFrom(value);
        if (result.IsSuccess) return result.Value;
        throw new ArgumentException(result.Error);
    }
}

实现领域特定的数值类型

Percentage类型添加范围验证和数学运算:

[ValueObject(typeof(decimal))]
public partial struct Percentage
{
    private static Validation Validate(decimal value)
    {
        if (value < 0 || value > 100)
            return Validation.Invalid("百分比必须在0-100之间");
        return Validation.Valid;
    }
    
    public static Percentage operator +(Percentage a, Percentage b)
    {
        return From(a.Value + b.Value);
    }
}

API契约中的价值对象应用

在ASP.NET Core API中使用价值对象作为参数类型,Swagger文档会自动显示正确的类型信息:

Swagger文档中的价值对象参数

控制器示例:

[HttpGet("{id}")]
public ActionResult<Customer> GetCustomer(CustomerId id)
{
    // id自动验证且类型安全
    var customer = _repository.GetById(id);
    return Ok(customer);
}

常见问题

Q: 如何处理价值对象的JSON序列化格式?
A: 使用[JsonConverter]属性自定义序列化行为:

[ValueObject(typeof(DateTime))]
[JsonConverter(typeof(MyCustomDateConverter))]
public partial struct BookingDate { }

Q: 能否为价值对象添加自定义方法?
A: 可以通过部分类扩展:

public partial struct CustomerId
{
    public string ToDatabaseString() => $"CUST-{Value:D8}";
}

Q: 如何在Entity Framework Core中使用价值对象?
A: 配置值转换器:

modelBuilder.Entity<Order>()
    .Property(o => o.OrderId)
    .HasConversion(
        id => id.Value,
        value => OrderId.From(value)
    );

Vogen通过将业务规则嵌入类型定义,使代码兼具安全性和表达力。从简单的ID封装到复杂的领域模型,Vogen都能帮助开发者构建更健壮、更易维护的.NET应用程序。无论是小型工具还是企业级系统,引入价值对象都是提升代码质量的有效实践。

登录后查看全文
热门项目推荐
相关项目推荐