C# 干净体系结构中的值对象是否可以依赖于接口

C# 干净体系结构中的值对象是否可以依赖于接口,c#,clean-architecture,value-objects,C#,Clean Architecture,Value Objects,我决定用干净的体系结构编写我的新项目,我对它不太熟悉。我正在使用这个模板 我有一个实体drawing.cs,它有一个BTCAddress ReceiveAddress属性: public class Withdrawal { ///ctor... public Guid Id { get; private set; } public decimal FiatAmountRequested { get; private set; } public Currenc

我决定用干净的体系结构编写我的新项目,我对它不太熟悉。我正在使用这个模板

我有一个实体
drawing.cs
,它有一个
BTCAddress ReceiveAddress
属性:

public class Withdrawal
{
    ///ctor...

    public Guid Id { get; private set; }

    public decimal FiatAmountRequested { get; private set; }
    public Currency FiatCurrency { get; private set; }

    public BTCAddress ReceiveAddress { get; private set; }
}
我将
BTCAddress
提取到ValueObject中,因为BTC地址不是普通字符串,所以需要验证逻辑

btcadress.cs

public class BTCAddress
{
    public BTCAddress(string address)
    {
        Address = address;
    }

    public string Address { get; private set; }

    public override string ToString()
    {
        return Address;
    }
}
如您所见,我尚未对地址实施任何验证。我必须使用外部库才能进行验证。我的问题是我是否可以在
btcadAddress
中添加一个依赖项,比如说
IBTCAddressValidator
,并在基础架构层实现该接口,或者它不允许依赖域层中的接口

如果不是,我想我应该在每个必须创建
btcadAddress
对象的用例(例如CreateDrawalRequestUseCase)中都具有该接口的依赖性。或者创建一个
createbtcadressusecase
,让其他用例依赖于它,这对我来说并不合适。或者创建某种帮助器类来创建此值对象

在我的值对象中添加外部库验证的正确方法是什么

编辑:

这是我的
requestdrawalcommand

using System;
using System.Threading;
using System.Threading.Tasks;
using AutoMapper;
using DepositWithdraw.Application.Common.Interfaces;
using DepositWithdraw.Domain.Entities;
using DepositWithdraw.Domain.ValueObjects;
using FluentValidation;
using MediatR;

namespace DepositWithdraw.Application.Withdrawals.Commands.RequestWithdrawal
{
    public class RequestWithdrawalCommand : IRequest, IMapTo<Withdrawal>
    {
        public string ExternalId { get; set; }
        public DateTime ExternalDateTime { get; set; }

        public Client Client { get; set; }

        public decimal FiatAmountRequested { get; set; }
        public FiatCurrency FiatCurrency { get; set; }

        public URL CallbackUrl { get; set; }
        public BTCAddress ReceiveAddress { get; set; }

        public class RequestWithdrawalCommandValidator : AbstractValidator<RequestWithdrawalCommand>
        {
            public RequestWithdrawalCommandValidator(IBTCAddressValidator btcAddressValidator)
            {
                RuleFor(p => p.FiatAmountRequested)
                    .GreaterThan(0)
                    .WithMessage("Requested fiat amount must be positive.");

                RuleFor(p => p.ReceiveAddress)
                    .Must(address => address != null && btcAddressValidator.IsValid(address.ToString()))
                    .WithMessage("Invalid BTC address.");
            }
        }

        public class RequestWithdrawalCommandHandler : AsyncRequestHandler<RequestWithdrawalCommand>
        {
            private readonly IApplicationDbContext _db;
            private readonly IRatesRepository _ratesRepository;
            private readonly IBTCWalletService _BTCWalletService;
            private readonly IMapper _mapper;

            public RequestWithdrawalCommandHandler(
                IApplicationDbContext db,
                IRatesRepository ratesRepository,
                IBTCWalletService BTCWalletService,
                IMapper mapper)
            {
                _db = db;
                _ratesRepository = ratesRepository;
                _BTCWalletService = BTCWalletService;
                _mapper = mapper;
            }

            protected override async Task Handle(RequestWithdrawalCommand request, CancellationToken cancellationToken)
            {
                var btcRate = _ratesRepository.GetBtcRate(request.Client.Id, request.FiatCurrency.ToString());
                var btcAmount = request.FiatAmountRequested / btcRate.BuyRate;

                var withdrawal = _mapper.Map<Withdrawal>(request);
                await _BTCWalletService.SendBTC(request.ReceiveAddress, btcAmount);
            }
        }
    }
}
使用系统;
使用系统线程;
使用System.Threading.Tasks;
使用自动制版机;
使用.Application.Common.Interfaces;
使用depositdraw.Domain.Entities;
使用DepositRetrach.Domain.ValueObjects;
使用FluentValidation;
使用MediatR;
命名空间DepositDraw.Application.Drawals.Commands.RequestDrawing
{
公共类请求撤回命令:IRequest,IMapTo
{
公共字符串外部ID{get;set;}
公共日期时间外部日期时间{get;set;}
公共客户端{get;set;}
请求的公共十进制fiatamount{get;set;}
公共FiatCurrency FiatCurrency{get;set;}
公共URL回调URL{get;set;}
公共BTCAddress接收地址{get;set;}
公共类RequestDrawalCommandValidator:AbstractValidator
{
公共请求撤回命令验证程序(IBTCAddressValidator BTCADressValidator)
{
规则(p=>p.FiatAmountRequested)
.大于(0)
.WithMessage(“请求的法定金额必须为正”);
规则(p=>p.ReceiveAddress)
.Must(address=>address!=null&&btcAddressValidator.IsValid(address.ToString()))
.WithMessage(“无效BTC地址”);
}
}
公共类RequestDruchAlCommandHandler:AsyncRequestHandler
{
私有只读IApplicationDbContext_db;
私有只读IRatesRepository _ratesRepository;
专用只读IBTCWalletService_BTCWalletService;
专用只读IMapper\u映射器;
公共请求撤回命令处理程序(
IApplicationDbContext数据库,
伊拉特储备率为负,
IBTCWalletService BTCWalletService,
IMapper(映射器)
{
_db=db;
_ratesRepository=ratesRepository;
_BTCWalletService=BTCWalletService;
_映射器=映射器;
}
受保护的覆盖异步任务句柄(RequestDrawalCommand请求、CancellationToken CancellationToken)
{
var btcRate=_ratesresposition.GetBtcRate(request.Client.Id,request.FiatCurrency.ToString());
var btcAmount=request.FiatAmountRequested/btcRate.BuyRate;
var撤销=_mapper.Map(请求);
wait_BTCWalletService.SendBTC(request.ReceiveAddress,btcAmount);
}
}
}
}

此代码按预期工作,验证了
ReceiveAddress
,但我不喜欢在使用
btcadress.cs
的任何地方都必须重复此验证过程。在这种情况下,我看不到在ValueObject中提取它的好处,我可以实现同样的效果,将
ReceiveAddress
保留为字符串。

类似的事情可能会变得非常固执己见。您可以始终将验证器分开,并在使用它的位置验证值对象。通过这种方式,它可以分离关注点。您能否提供有关此用例的更多细节,以及它将如何使用验证器,以便我们更清楚地了解全局情况。@Nkosi客户调用我的API,请求将一些比特币提取到给定的地址。在
createDrawcalRequestUseCase
中,我想在向他发送比特币之前验证他给我的地址。如果你愿意,我可以更详细地说,但简而言之,就这些。在我的理解中,最好是
btcadAddress
知道它什么时候有效,什么时候无效。但是验证逻辑又长又复杂,我想使用一个外部库来更新显示的代码,使用您刚才解释的内容。
但是验证逻辑又长又复杂。更有理由让它为自己服务