28

ServiceStackのでは、最初に ASP.NET MVC Web サイトを作成し、次に ServiceStack サービスを作成した単一のアプリケーションは見当たりません。

ビューを介して製品をレンダリングする非常に単純な ASP.NET MVC Web アプリケーションを考えてみましょう。コントローラー、ビュー、モデル、ビューモデルを使用します。

Productドキュメント DB に永続化されるモデルがあるとします。MVC Razor View/PartialView からマップされて表示さProductViewModelれるビューモデルがあるとします。Product

これは Web 側の話です。ここで、Windows 8 アプリケーションなどのさまざまなクライアントに製品を返すサービスを追加するとします。

要求/応答クラスは、既にあるものから完全に切り離すべきですか? ProductViewModelサービスから返したいものがすべて含まれている可能性があります。

既にProduct(モデル クラス) を持っているのでProduct、API 名前空間に別のクラスを含めることはできません。

では、API 名前空間にスタンドアロンProductRequestクラスとProductRequestResponse(ProductViewModel を継承する) クラスを導入する必要がありますか?

そうProductRequestResponse : ProductViewModelですか?

私が言いたいのは、すでに Model クラスと ViewModel クラスがあり、SS サービスの Request クラスと Response クラスを構築するには、主に既存のクラスからすべてをコピーして、さらに 2 つのファイルを作成する必要があるということです。これは私には DRY に見えません。懸念事項の分離のガイドラインに従っているかもしれませんが、DRY も重要であり、実際にはすべてを分離するだけではありません (すべてを分離するとコードの重複につながります)。

私が見たいのは、Web アプリケーションが既に作成されており、現在モデルとビューモデルを備えており、Web 上で表示するための適切なビューを返しますが、プログラム クライアントをサポートするために完全に機能するサービスに拡張できる場合です。AJAX クライアントなどのように...既に持っているものを使用します。

別物:

この例を見るとhttps://github.com/ServiceStack/ServiceStack.Examples/blob/master/src/ServiceStack.MovieRest/MovieService.cs

MovieRequest クラスとMoviesRequest クラス (1 つは単一のムービー リクエスト用、もう 1 つはムービーのリスト用)があることがわかります。そのため、2 つのサービスもMovieServiceありMoviesService、1 つは単一の映画のリクエストを処理し、もう 1 つは映画のジャンルを処理します。

さて、私はサービスに対する SS のアプローチが好きで、それが正しいと思いますが、単に要求の種類という理由だけでこの種の分離は好きではありません。監督別の映画が必要な場合はどうすればよいですか? Directorプロパティとそのためのさらに別のサービス ( ) を持つさらに別の要求クラスを発明MoviesByDirectorするでしょうか?

サンプルは 1 つのサービスに向けられるべきだと思います。映画に関係するものはすべて 1 つの屋根の下にある必要があります。ServiceStackでそれをどのように達成しますか?

public class ProductsService : Service
{
    private readonly IDocumentSession _session;
    private readonly ProductsHelperService _productsHelperService;
    private readonly ProductCategorizationHelperService _productCategorization;

    public class ProductRequest : IReturn<ProductRequestResponse>
    {
        public int Id { get; set; }
    }

    // Does this make sense? 
    // Please note, we use ProductViewModel in our Views and it holds everything we'd want in service response also
    public class ProductRequestResponse : ProductViewModel
    {
    }

    public ProductRequestResponse GetProducts(ProductRequest request)
    {
        ProductRequestResponse response = null;
        if (request.Id >= 0)
        {
            var product = _session.Load<Product>(request.Id);
            response.InjectFrom(product);
        }
        return response;
    }
}
4

2 に答える 2

70

サービス層は最も重要な契約です

システム全体で作成できる最も重要なインターフェイスは、外部向けのサービス コントラクトです。これは、サービスまたはアプリケーションのコンシューマーがバインドするものです。つまり、コードとともに更新されないことが多い既存の呼び出しサイトです。 -base - 他のすべてのモデルはセカンダリです。

DTO はリモート サービスのベスト プラクティスです

リモート サービス ( MSDN ) にDTO (データ転送オブジェクト) を使用するための Martin Fowler の推奨に従ってServiceStackはクリーンで汚染されていない POCO の使用を推奨し、明確に定義されたコントラクトを定義します。 dll. この利点により、C#/.NET クライアントでサービスを定義するために使用される型付き DTO をそのまま再利用できるようになり、コードを使用せずにエンドツーエンドの型付き API を提供できます。ゲンまたは他の人工機械。

DRY vs インテント

物事を DRY に保つことは、目的を明確に述べるのと混同しないでください。DRY を試みたり、継承、魔法のプロパティ、またはその他のメカニズムの背後に隠れたりすることは避けてください。明確で明確に定義された DTO を使用すると、誰もが参照して各サービスが何を受け入れ、何を返すかを確認できる単一の参照ソースが提供されます。これにより、クライアントとサーバーの開発者はすぐに作業を開始し、実装せずに外部サービス モデルにバインドできます。書かれています。

DTO を分離しておくことで、外部クライアントを壊すことなく内部から実装を自由にリファクタリングすることもできます。つまり、サービスが応答のキャッシュを開始したり、NoSQL ソリューションを利用して応答を設定したりできます。

また、自動生成されたメタデータ ページ、サンプル レスポンス、Swagger サポート、XSD、WSDL などを作成するために使用される信頼できるソース (アプリ ロジック内でリークまたは結合されていない) も提供します。

ServiceStack の組み込み自動マッピングの使用

個別の DTO モデルを保持することをお勧めしますが、 AutoMapperなどのマッパーを使用したり、ServiceStack の組み込みの自動マッピング サポートを使用したりできるため、独自の手動マッピングを維持する必要はありません。

viewModel の一致するプロパティが設定された新しい DTO インスタンスを作成します。

var dto = viewModel.ConvertTo<MyDto>();

DTO を初期化し、ビュー モデルの一致するプロパティを設定します。

var dto = new MyDto { A = 1, B = 2 }.PopulateWith(viewModel);

DTO を初期化し、ビュー モデルのデフォルト以外の一致するプロパティを設定します。

var dto = new MyDto { A = 1, B = 2 }.PopulateWithNonDefaultValues(viewModel);

DTO を初期化し、ビュー モデルのAttr属性で注釈が付けられた一致するプロパティを設定します。

var dto = new MyDto { A=1 }.PopulateFromPropertiesWithAttribute<Attr>(viewModel);

マッピング ロジックがより複雑になると、拡張メソッドを使用してコードを DRY に保ち、アプリケーション内から簡単に使用できる 1 つの場所にマッピングを維持することが好きです。

public static class MappingExtensions
{
    public static MyDto ToDto(this MyViewModel viewModel)
    {
        var dto = viewModel.ConvertTo<MyDto>();
        dto.Items = viewModel.Items.ConvertAll(x => x.ToDto());
        dto.CalculatedProperty = Calculate(viewModel.Seed);
        return dto;
    }
}

これは、次のものだけで簡単に消費できるようになりました。

var dto = viewModel.ToDto();
于 2013-03-12T18:54:35.040 に答える
2

特にServiceStackに縛られておらず、「プログラムクライアントをサポートするための完全に機能するサービス...すでに持っているもの」が必要な場合は、次のことを試すことができます。リクエストのacceptヘッダーに基づいて、コントローラーに aViewResultまたは aを返すようにします-または。 JsonResultRequest.AcceptTypes.Contains("text/html")Request.AcceptTypes.Contains("application/json")

ViewResultと は両方とも でJsonResultあるためActionResult、アクションのシグネチャは同じままで、 と の両方が ViewModelView()を受け入れます。さらに、ControllerBase がある場合は、View() または Json() のいずれかを呼び出すJson()基本メソッド (たとえば ) を作成できるため、既存のコードへの変更は最小限に抑えられます。protected ActionResult RespondWith(Object viewModel)

もちろん、ViewModel が純粋でない場合 (つまり、html 固有のものがあるか、ViewBag マジックに依存している場合) は、もう少し作業が必要です。また、SOAP や ServiceStack によって提供されるその他のバインディング タイプを取得することはできませんが、既存の MVC アプリへの最小限のコード変更で JSON データ インターフェイスをサポートすることが目標である場合、これは解決策になる可能性があります。

LP

于 2013-04-10T22:36:50.610 に答える