"不要在设计中使用抽象基类; 但在建模/分析中"

LCJ*_*LCJ 9 .net c# wcf soa ooad

虽然我在OOAD有一些经验,但我是SOA的新手.

SOA设计的一个指导原则是"仅使用抽象类进行建模".从设计中忽略它们.抽象的使用可以有助于建模(分析阶段).

在分析阶段,我想出了一个BankAccount基类.从中派生的专业类是"FixedAccount"和"SavingsAccount".我需要创建一个服务,返回用户的所有帐户(帐户列表).应满足要求的服务结构应该是什么?

注意:如果您可以使用WCF提供代码演示,那将会很棒.

jag*_*all 8

听起来您正在尝试使用SOA来远程访问对象模型.您最好查看希望服务公开的交互和功能,并避免暴露服务实现的继承详细信息.

因此,在这种情况下,您需要一个用户帐户列表,您的界面看起来就像

[ServiceContract]
interface ISomeService
{
    [OperationContract]
    Collection<AccountSummary> ListAccountsForUser(
        User user /*This information could be out of band in a claim*/);
}

[DataContract]
class AccountSummary
{
     [DataMember]
     public string AccountNumber {get;set;}
     [DataMember]
     public string AccountType {get;set;}
     //Other account summary information
}
Run Code Online (Sandbox Code Playgroud)

如果你决定继续继承路由,你可以使用KnownType属性,但要注意这会在通过网络发送的消息中添加一些类型信息,这可能会限制你在某些情况下的互操作性.

更新:

当我回答时,我的时间有点受限,所以我会试着详细说明为什么我更喜欢这种风格.

我不建议通过DTO在单独的图层中暴露你的OOAD,这通常会导致一个膨胀的界面,你传递大量未使用的数据并将其重新映射到你的域模型的副本中删除所有逻辑,我只是​​没有看到值.我通常围绕它公开的操作设计我的服务层,并使用DTO来定义服务交互.

使用基于公开操作而不是域模型的DTO有助于保持服务封装并减少与域模型的耦合.通过不暴露我的域模型,我不必为了序列化而在字段可见性或继承上做出任何妥协.

例如,如果我将Transfer方法从一个帐户暴露给另一个帐户,则服务接口看起来像这样:

[ServiceContract]
interface ISomeService
{
    [OperationContract]
    TransferResult Transfer(TransferRequest request);
}

[DataContract]
class TransferRequest
{
     [DataMember]
     public string FromAccountNumber {get;set;}
     [DataMember]
     public string ToAccountNumber {get;set;}
     [DataMember]
     public Money Amount {get;set;}
}

class SomeService : ISomeService
{
    TransferResult Transfer(TransferRequest request)
    {
        //Check parameters...omitted for clarity
        var from = repository.Load<Account>(request.FromAccountNumber);
        //Assert that the caller is authorised to request transfer on this account
        var to = repository.Load<Account>(request.ToAccountNumber);
        from.Transfer(to, request.Amount);
        //Build an appropriate response (or fault)
    }
}
Run Code Online (Sandbox Code Playgroud)

现在从这个界面可以清楚地看到conusmer调用此操作所需的数据是什么.如果我实现这个

[ServiceContract]
interface ISomeService
{
    [OperationContract]
    TransferResult Transfer(AccountDto from, AccountDto to, MoneyDto dto);
}
Run Code Online (Sandbox Code Playgroud)

和AccountDto是帐户中的字段的副本,作为消费者,我应填充哪些字段?他们都是?如果添加新属性以支持新操作,则所有操作的所有用户现在都可以看到此属性.WCF允许我将此属性标记为非强制性,这样我就不会破坏所有其他客户端,但如果它对新操作是必需的,则客户端只会在调用操作时找到它.

更糟糕的是,作为服务实施者,如果他们为我提供了当前余额会发生什么?我应该相信吗?

这里的一般规则是询问谁拥有数据,客户或服务?如果客户端拥有它,那么它可以将它传递给服务,并且在进行一些基本检查之后,服务可以使用它.如果服务拥有它,则客户端应该只传递足够的服务信息以检索它所需的内容.这允许服务维护其拥有的数据的一致性.

在此示例中,服务拥有帐户信息,并且用于定位帐户的密钥是帐号.虽然服务可以验证金额(是正数,支持的货币等),但这是由客户拥有的,因此我们期望填充DTO上的所有字段.

总而言之,我已经看到它完成了所有3种方式,但围绕特定操作设计DTO迄今为止在服务和消费者实现方面都是最成功的.它允许操作独立发展,并且非常明确地表示服务的期望以及将返回给客户端的内容.