虽然我在 OOAD 方面有一些经验,但我是 SOA 的新手。
SOA 设计的准则之一是“仅将抽象类用于建模。从设计中省略它们”。抽象的使用有助于建模(分析阶段)。
在分析阶段,我想出了一个 BankAccount 基类。从它派生的专门类是“FixedAccount”和“SavingsAccount”。我需要创建一个服务来返回用户的所有帐户(帐户列表)。满足要求的服务结构应该是什么?
注意:如果您能提供使用WCF 的代码演示,那就太好了。
最佳答案
听起来您正在尝试使用 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
}
如果您决定走继承路线,您可以使用 KnownType attribute ,但请注意,这会将一些类型信息添加到通过线路发送的消息中,这在某些情况下可能会限制您的互操作性。
更新:
我之前回答时时间有限,所以我会尝试详细说明为什么我喜欢这种风格。
我不建议通过 DTO 在单独的层中公开您的 OOAD,这通常会导致界面臃肿,您在其中传递大量未使用的数据,并虔诚地将其映射到本质上是您的域模型删除了所有逻辑,我只是看不到它的值(value)。我通常围绕它公开的操作设计我的服务层,并且我使用 DTO 来定义服务交互。
使用基于公开操作而非域模型的 DTO 有助于保持服务封装并减少与域模型的耦合。通过不公开我的域模型,我不必为了序列化而在字段可见性或继承方面做出任何妥协。
例如,如果我将一个 Transfer 方法从一个帐户公开到另一个帐户,服务接口(interface)将如下所示:
[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)
}
}
现在从这个界面上,消费者可以很清楚调用这个操作所需的数据是什么。如果我将其实现为
[ServiceContract]
interface ISomeService
{
[OperationContract]
TransferResult Transfer(AccountDto from, AccountDto to, MoneyDto dto);
}
而AccountDto是account中字段的副本,作为消费者,我应该填充哪些字段?他们全部?如果添加新属性以支持新操作,则所有操作的所有用户现在都可以看到此属性。 WCF 允许我将此属性标记为非强制性属性,这样我就不会破坏我的所有其他客户端,但如果它对新操作是强制性的,则客户端只有在调用该操作时才会发现。
更糟糕的是,作为服务实现者,如果他们向我提供了当前余额会怎样?我应该相信它吗?
这里的一般规则是询问谁拥有数据,客户还是服务?如果客户端拥有它,那么它可以将它传递给服务,在进行一些基本检查后,服务可以使用它。如果服务拥有它,客户端应该只传递足够的信息让服务检索它需要的信息。这允许服务保持其拥有的数据的一致性。
在这个例子中,服务拥有账户信息,定位它的键是一个账号。虽然该服务可能会验证金额(正数、支持的货币等),但这是由客户拥有的,因此我们希望 DTO 上的所有字段都被填充。
总而言之,我已经看到它以所有 3 种方式完成,但围绕特定操作设计 DTO 是迄今为止最成功的服务和消费者实现方式。它允许操作独立发展,并且非常明确地说明服务期望什么以及将返回给客户端的什么。
关于c# - “Do not use Abstract Base class in Design; but in Modeling/Analysis”,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9470013/