Вопрос о SOA: раскрытие сущностей - PullRequest
2 голосов
/ 07 октября 2010

Я хотел бы включить шаблон SOA в мою трехуровневую структуру.Я создал слой службы (узел WCF) между BLL и пользовательским интерфейсом.Моя структура теперь выглядит следующим образом:

UI <> WCF <> BLL <> DAL

   <---[Entities] --->

Проблема в том, что ямои сущности находятся в отдельной DLL (и это было видно во ВСЕХ слоях, кроме как в пользовательском интерфейсе). Теперь мне нужно выставить его так, чтобы потребитель моей службы мог его использовать. В этом случае, пользовательский интерфейс.Как я могу это сделать?

Entities.DLL

   namespace Entities 
   {
       public class Account
       {
           public string AcctID { get; set; }
           public string AcctName { get; set; }
       }
    }

сейчас, я планирую использовать его в WCF

Уровень интерфейса службы

    public class AccountService : IAccountService
    {

         public Account GetAccount(string AcctID)
         { 
             //fetch  from DAL through BLL
          }
     }

Можно ли просто приписывать мои сущности?(Обратите внимание, я также использую объекты в DAL и BLL)

  using System.Runtime.Serialization;
   namespace Entities 
   {
      [DataContract]
       public class Account
       {
          [DataMember]
           public string AcctID { get; set; }

          [DataMember]
           public string AcctName { get; set; }
       }
    }

Любое предложение, ребята?

Ответы [ 5 ]

3 голосов
/ 07 октября 2010

Вот система, которая работает для нас:

Обычно вы должны использовать объект передачи данных, который отражает ожидаемые данные на стороне клиента. Бизнес-уровень должен определять эти DTO вместе с их интерфейсами репозитория. Уровень данных должен реализовывать интерфейсы репозитория, преобразовывая ваши объекты уровня данных в DTO. Уровень WCF должен быть просто внешней оболочкой для ваших различных методов интерфейса репозитория.

Таким образом, это выглядит примерно так:

 UI ---\ 
  |     BLL   --   DAL
 WCF---/
    [     DTO    ]
    [Repositories]
                 [Entities]

По моему мнению, уровень WCF является частью уровня пользовательского интерфейса, поэтому я чувствовал бы себя хорошо, если бы они оба знали объекты, определенные на бизнес-уровне. Однако вы можете сделать еще один шаг вперед и сделать так, чтобы слой WCF отвечал за преобразование ваших бизнес-объектов в DTO:

 UI   --   WCF   --   BLL   --   DAL
 [    DTOs   ]
         [      Repositories      ]
         [    Business Objects    ]
                              [Entities]

Таким образом, каждый слой знает не более одного слоя с каждой стороны от него. DTO могут быть аннотированы для сериализации или чего-то еще, потому что они действительно предназначены только для этой цели. Только уровень доступа к данным знает о ваших сущностях данных.

В ответе на ваш комментарий:

Если ваши сущности определены на уровне доступа к данным, то они действительно не DTO. Они моделируют ваш слой данных, который не обязательно переводится непосредственно в нужные вам объекты в пользовательском интерфейсе.

Причина, по которой я предлагаю определять интерфейсы для ваших репозиториев, заключается в том, что вы можете использовать внедрение зависимостей, чтобы ослабить связь между вашим уровнем WCF и вашим бизнес-уровнем. Это также поможет сделать ваш уровень WCF модульно-тестируемым, потому что вы можете создавать фальшивые или фиктивные реализации репозитория, которые имитируют определенную ситуацию.

В первой модели, которую я рекомендовал, ваши методы WCF будут выглядеть почти точно так же, как ваши методы репозитория, поэтому обычный WCF будет просто "оборачивать" метод репозитория:

public IEnumerable<Task> GetActiveTasks() {
    return _taskRepository.GetActiveTasksForUser(_sessionManager.CurrentUser);
}
1 голос
/ 07 октября 2010

DTO - очень хороший подход, и в некоторых случаях они абсолютно необходимы.Вот некоторые из этих сценариев:

  • Большие проекты - идут вместе с другими сценариями.Разделение проблем важно.
  • Объекты являются объектами домена = содержит бизнес-логику
  • Объекты так или иначе связаны с .NET, и сервис должен использоваться с других платформ
  • Предоставляет сервисный уровеньспециализированные типы данных для каждой операции вместо интерфейсов CRUDy.Например, операция выбора может вернуть объект с данными, такими как дата создания, дата последнего изменения и т. Д. Но операция обновления не требует передачи этих данных от клиента, поэтому она использует DTO без этих полей.Обычно вы идете даже дальше, и у вас нет чистого выбора и обновления, но есть некоторые реальные бизнес-функции.

С другой стороны, ваша архитектура должна зависеть от ваших требований и ожидаемой сложности и размера вашего приложения.,DTO включает в себя много дополнительной работы = дополнительные расходы.В небольшом простом проекте, где ваш сервис будет использоваться только вашим клиентом пользовательского интерфейса, написанным на .NET, нет ничего плохого в том, чтобы определять ваши сущности в отдельной сборке, отмечая эти сущности атрибутами для сериализации и аннотациями данных (может использоваться для проверки обоихсо стороны клиента и сервера) или с другими атрибутами проверки (например, блок приложения проверки из библиотеки Enterprise) и совместное использование этой сборки всеми уровнями приложения, включая клиент пользовательского интерфейса.Сначала простота.

1 голос
/ 07 октября 2010

Я думаю, что получил его с помощью AutoMapper.

Я наконец выставляю сущность как DTO через WCF ... простой способ ..

Сущность

 namespace Entities
 { 
   public class Account
   {
    public string AccountNo { get; set; }
    public string AccountName { get; set; }
   }
 }

BLL

 namespace BLL
 {
   // This defines the repository interface.
   public interface IAccountRepository
   {
     Account GetAccount(int accountId);
   }

   public class AccountRepository
   {
     public Account GetAccount(int accountId) {
       // get the Account object from the data layer.
     }
   }

   // Using an interface makes it easy to swap various implementations.
   // The implementation above would be the one you'd normally use, but you could
   // create others like this to help with unit testing and such.
   public class FakeAccountRepository : IAccountRepository
   {
    public Account GetAccount(int accountId)
    {
        return new Account { AccountName = "JuanDeLaCruz", AccountNo = "123456" };
    }
   }
 }

WCF

[ServiceContract]
public interface IService
{
   [OperationContract]
   AccountDTO GetAccount(int accountId);
}


 [DataContract]
 public class AccountDTO
 {
  [DataMember]
  public string AccountNo { get; set; }
  [DataMember]
  public string AccountName { get; set; }
 }


 public class Service : IService
 {
   // Define a Factory in your .svc file to inject a repository implementation.
   // It's best to use an IoC container like Ninject for this sort of thing.
   public Service( // no pun intended
       IAccountRepository accountRepository)
   {
     _accountRepository = accountRepository
   }

   public AccountDTO GetAccount(int accountId)
   {
     Mapper.CreateMap<Account, AccountDTO>();
     var account = _accountRepository.GetAccount(accountId);
     var accountDto = Mapper.Map<Account, AccountDTO>(account);
     return account;
   }
 }

WCF Aspx Consumer

 protected void Page_Load(object sender, EventArgs e)
 {
    ServiceClient accountService= new ServiceClient();
    AccountDTO account = accountService.GetAccount();
    Response.Write(account.AccountName);
 }

Пожалуйста, комментируйте любые предложения / исправления, ребята .. ^^

СПАСИБО сэру Стиффлингу и Гоблину

1 голос
/ 07 октября 2010

Я могу рассказать вам, как я это делаю.

У меня есть отдельные DTO и сущности - и это не всегда соотношение 1: 1.Я действительно не люблю иметь все атрибуты в моих сущностях.(Кроме того, он нарушает инкапсуляцию, так как все свойства внезапно становятся доступными для чтения и записи.

Если вам нужно простое преобразование между этими двумя типами - есть библиотеки, облегчающие (например) AutoMapper.

Есливы используете сущности в качестве DTO, вы часто будете отправлять слишком много данных - например, Ордера, имеющего Учетную запись с несколькими OpenOrders типа Order. Каждый раз, когда вы получаете один ордер, вы также получаете все открытые ордера Учетной записи.

Во-вторых, я бы использовал тот же business-dll в пользовательском интерфейсе, который я использую на уровне обслуживания, - чтобы я мог проверить его на стороне клиента, прежде чем отправлять его на сервер. Конечно, эта часть не является обязательной - вы также можетепродублируйте логику (но я также ненавижу дублирование: -)).

Надеюсь, это даст вам немного направления.

0 голосов
/ 08 октября 2010

Если вы хотите выставить контракт на обслуживание на клиенте, самый простой способ - это

[Serializable]
public class Accountd
{
    public string AccountNo { get; set; } 
    public string AccountName { get; set; } 
}

и если вам нужно больше контроля над тем, какой член должен пересечь границу, используйте это

[DataContract]
public class Account
{
    [DataMember]
    public string AccountNo { get; set; } 
    public string AccountName { get; set; }  **// client won't see this data.**
}
...