Заполнение свойств ассоциации в объектах из вызова службы - PullRequest
0 голосов
/ 06 мая 2011

Скажем, у меня есть общий шаблон с объектом Customer и объектом SalesOrder. У меня есть соответствующие объекты SalesOrderContract и CustomerContract, которые похожи, более плоские объекты, используемые для сериализации через веб-сервис

public class Customer 
{
     public int CustomerId { get; set; }
     public string Name { get; set; }
     public Address ShippingAddress { get; set; }
     //more fields...
}

public class Order
{
     public int OrderId { get; set; }
     public Customer Customer { get; set;
     // etc
}

И мой контракт на продажу выглядит так

public class OrderContract
{
     public int OrderId { get; set; }
     public int CustomerId { get; set; }
}

public class OrderTranslator
{
     public static Order ToOrder(OrderContract contract)
     {
          return new Order { OrderId = contract.OrderId  };
          // just translate customer id or populate entire Customer object
     }
 }

У меня есть слой между уровнем сервиса и уровнем бизнес-объекта, который переводится между ними. Мой вопрос заключается в следующем ... нужно ли заполнять объект Order.Customer на другом конце, так как для таблицы Order нужен только идентификатор клиента. Я не несу весь объект клиента в OrderContract, потому что это не нужно и слишком тяжело. Но, как часть его сохранения, я должен подтвердить, что это действительно действующий клиент. Я могу сделать несколько вещей

  1. Заполните объект Order.Customer полностью на основе CustomerId, когда я выполняю перевод между контрактом и сущностью. Это потребует вызова CustomerRepository во вспомогательном классе, который переводит между сущностями и контрактами. Не чувствует себя хорошо для меня. Переводчик действительно должен быть отображением данных.
  2. Создание доменной службы для каждой группы операций, которая выполняет необходимую проверку без заполнения Order.Customer. Эта служба будет извлекать объект Customer на основе Order.CustomerId и проверять, действителен ли он. Не уверен в этом, потому что заказ на продажу должен быть в состоянии подтвердить себя, но он также не имеет прямого отношения к заказам, так как он также имеет дело с клиентами, так что, возможно, служба домена?
  3. Создайте отдельное свойство Order.CustomerId и отложите загрузку объекта клиента на основе этого.
  4. Заполнить заказ. Заказчик с фабричного класса. Прямо сейчас мои фабричные классы только для загрузки из базы данных. Я не загружаюсь из контрактов, но, может быть, это имеет смысл?

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

Суть в том, что мне нужно иметь возможность сделать что-то вроде

 if (order.Customer == null || !order.Customer.IsActive)
 {
      //do something
 }

Вопрос в том, где имеет смысл это делать? На самом деле в моем объекте Order есть много дочерних объектов, необходимых для проверки, и я не хочу, чтобы вещи стали раздутыми. Вот почему я рассматриваю возможность создания доменных служб для инкапсуляции проверки, поскольку в моем конкретном случае это такая огромная операция (несколько сотен странных правил). Но я также не хочу удалять всю логику, делая мои объекты просто свойствами. Найти баланс сложно.

Надеюсь, это имеет смысл. Если требуется больше информации, дайте мне знать.

1 Ответ

0 голосов
/ 19 мая 2011

У вас здесь происходит пара вещей. Я думаю, что отчасти проблема заключается в том, как вы организовали свой класс переводчиков. Помните, что для сущности вся концепция основана на идентичности экземпляра. Таким образом, Переводчик для сущности не должен возвращать новый объект, он должен возвращать правильный экземпляр объекта. Как правило, это означает, что вы должны сначала предоставить его этому экземпляру.

Возможно, полезно думать об обновлениях по сравнению с созданием нового объекта.

Для обновления я бы структурировал эту операцию следующим образом: у меня был бы веб-сервис, который приложение вызывает для получения и возврата объектов контракта. Этот веб-сервис вызывает и репозитории, и переводчики для выполнения своей работы. Проверка остается на доменном объекте.

В коде обновление будет выглядеть примерно так:

Веб-сервис:

[WebService]
public class OrderService
{
    [WebMethod]
    public void UpdateOrder(OrderContract orderContract)
    {
        OrderRepository orderRepository = new OrderRepository(_session);

        // The key point here is we get the actual order itself
        // and so Customer and all other objects are already either populated
        // or available for lazy loading.

        Order order = orderRepository.GetOrderByOrderContract(orderContract);

        // The translator uses the OrderContract to update attribute fields on
        // the actual Order instance we need.

        OrderTranslator.OrderContractToOrder(ref order, orderContract);

        // We now have the specific order instance with any properties updated
        // so we can validate and then persist.

        if (order.Validate())
        {
            orderRepository.Update(order);
        }
        else
        {
            // Whatever
        }
    }
}

Переводчик:

public static class OrderTranslator
{
    public static void OrderContractToOrder(ref Order order, OrderContract orderContract)
    {
        // Here we update properties on the actual order instance passed in
        // instead of creating a new Order instance.

        order.SetSomeProperty(orderContract.SomeProperty);
        // ... etc.
    }

}

Ключевой концепцией здесь является то, что у нас есть сущность, мы получаем фактический Order, экземпляр сущности, а затем используем транслятор для обновления атрибутов вместо создания нового экземпляра Order. Поскольку мы получаем исходный Заказ, а не создаем новый экземпляр, возможно, что все ассоциации могут быть либо заполнены, либо заполнены отложенной загрузкой. Нам не нужно воссоздавать какие-либо ассоциации из OrderContract, поэтому проблема устраняется.

Я думаю, что другой частью проблемы может быть ваше понимание того, как проектируется фабрика. Это правда, что для сущностей Фабрика может не устанавливать все возможные атрибуты - метод может стать безнадежно сложным, если он это сделает.

Но что фабрика должна делать, так это создавать все ассоциации для нового объекта, чтобы возвращаемый новый объект находился в действительном состоянии с точки зрения полной и допустимой совокупности. Затем вызывающая сторона может установить все другие различные и различные «простые» атрибуты.

Каждый раз, когда у вас есть Фабрика, вы должны принимать решение о том, какие параметры передавать. Возможно, в этом случае веб-сервис получает фактического Клиента и передает его на завод в качестве параметра. Или, может быть, веб-сервис передает Id, и фабрика отвечает за получение фактического экземпляра Customer. Он будет варьироваться в зависимости от конкретной ситуации, но в любом случае, несмотря на то, что он получает другие требуемые объекты, фабрика должна возвращать как минимум полностью заполненный объект в терминах своего графа, т.е. все отношения должны присутствовать и проходить.

В коде возможный пример создания нового Заказа может быть:

[WebService]
public class OrderService
{

    [WebMethod]
    public void SaveNewOrder(OrderContract orderContract)
    {
        // Lets assume in this case our Factory has a list of all Customers
        // so given an Id it can create the association.

        Order order = OrderFactory.CreateNewOrder(orderContract.CustomerId);

        // Once again we get the actual order itself, albeit it is new,
        // and so Customer and all other objects are already either populated
        // by the factory create method and/or are available for lazy loading.

        // We can now use the same translator to update all simple attribute fields on
        // the new Order instance.

        OrderTranslator.OrderContractToOrder(ref order, orderContract);

        // We now have the new order instance with all properties populated
        // so we can validate and then persist.

        if (order.Validate())
        {
            //Maybe you use a Repository - I use a unit of work but the concept is the same.
            orderRepository.Save(order);
        }
        else
        {
            //Whatever
        }
    }
}

Так, надеюсь, это поможет?

...