9

私はSOAの初心者ですが、OOADの経験はあります。

SOA設計のガイドラインの1つは、「モデリングにのみ抽象クラスを使用する」です。デザインからそれらを省略してください。」抽象化の使用は、モデリング(分析フェーズ)に役立ちます。

分析フェーズで、BankAccount基本クラスを考え出しました。そこから派生した特殊なクラスは「FixedAccount」と「SavingsAccount」です。ユーザーのすべてのアカウント(アカウントのリスト)を返すサービスを作成する必要があります。要件を満たすためのサービスの構造はどうあるべきですか?

注: WCFを使用してコードのデモンストレーションを提供できれば素晴らしいと思います。

4

2 に答える 2

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
}

継承ルートを使用する場合は、KnownType属性を使用できますが、これにより、ネットワークを介して送信されるメッセージにタイプ情報が追加され、相互運用性が制限される場合があることに注意してください。

更新

以前に答えたときは少し時間が限られていたので、なぜこのスタイルを好むのかを詳しく説明します。

OOADをDTOを介して別のレイヤーに公開することはお勧めしません。これにより、通常、使用されていない大量のデータを渡し、ドメインモデルの本質的なコピーとの間でデータを忠実にマッピングする、肥大化したインターフェイスが発生します。すべてのロジックが削除され、値が表示されません。私は通常、公開する操作を中心にサービスレイヤーを設計し、サービスの相互作用の定義にDTOを使用します。

ドメインモデルではなく公開された操作に基づいてDTOを使用すると、サービスのカプセル化を維持し、ドメインモデルへの結合を減らすことができます。ドメインモデルを公開しないことで、シリアル化のためにフィールドの可視性や継承について妥協する必要がなくなります。

たとえば、あるアカウントから別のアカウントへの転送メソッドを公開している場合、サービスインターフェイスは次のようになります。

[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は、コンシューマーとしてのアカウント内のフィールドのコピーです。どのフィールドに入力する必要がありますか?それらすべて?新しい操作をサポートするために新しいプロパティが追加された場合、すべての操作のすべてのユーザーがこのプロパティを表示できるようになります。WCFを使用すると、このプロパティを非必須としてマークして、他のすべてのクライアントを壊さないようにすることができますが、新しい操作に必須の場合、クライアントは操作を呼び出したときにのみ検出されます。

さらに悪いことに、サービスの実装者として、現在の残高が提供された場合はどうなりますか?私はそれを信頼すべきですか?

ここでの一般的なルールは、データ、クライアント、またはサービスの所有者を尋ねることです。クライアントがそれを所有している場合、それをサービスに渡すことができ、いくつかの基本的なチェックを行った後、サービスはそれを使用できます。サービスがそれを所有している場合、クライアントは、サービスが必要なものを取得するのに十分な情報のみを渡す必要があります。これにより、サービスは所有するデータの整合性を維持できます。

この例では、サービスがアカウント情報を所有しており、それを見つけるためのキーはアカウント番号です。サービスは金額(正、サポートされている通貨など)を検証する場合がありますが、これはクライアントが所有しているため、DTOのすべてのフィールドに入力されることが期待されます。

要約すると、3つの方法すべてが実行されていることを確認しましたが、特定の操作を中心にDTOを設計することは、サービスとコンシューマーの両方の実装で最も成功しています。これにより、操作を独立して進化させることができ、サービスによって何が期待され、何がクライアントに返されるかについて非常に明確になります。

于 2012-02-28T07:15:12.470 に答える
1

私は他の人がここで言ったこととほとんど同じですが、おそらくこれらを追加する必要があります:

  • ほとんどのSOAシステムは、通信にWebサービスを使用します。Webサービスは、WSDLを介してインターフェースを公開します。WSDLは継承を理解していません。
  • DTOのすべての動作は、DTOがネットワークを通過すると失われます
  • すべてのプライベート/保護されたフィールドは、ネットワークを通過すると失われます

このシナリオを想像してみてください(ケースはばかげていますが、実例です):

public abstract class BankAccount
{
    private DateTime _creationDate = DateTime.Now;

    public DateTime CreationDate
    {
        get { return _creationDate; }
        set { _creationDate = value; }
    }

    public virtual string CreationDateUniversal
    {
        get { return _creationDate.ToUniversalTime().ToString(); }
    }
}

public class SavingAccount : BankAccount
{
    public override string CreationDateUniversal
    {
        get
        {
            return base.CreationDateUniversal + " UTC";
        }
    }
}

これで、クライアントで「サービス参照の追加」または「Web参照の追加」を使用して(アセンブリを再利用せずに)、普通預金口座にアクセスしました。

SavingAccount account = serviceProxy.GetSavingAccountById(id);
account.CreationDate = DateTime.Now;
var creationDateUniversal = account.CreationDateUniversal; // out of sync!!

何が起こるかというと、サーバーでのシリアル化時の値だけがネットワークを通過する実装がないため、への変更はCreationDate返送されません。CreationDateUniversalCreationDateUniversal

于 2012-02-28T10:31:16.310 に答える