3/14/2007 9:27:00 AM

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

Писать этот постинг меня сподвигло то, что сразу с двумя человеками из разных компаний и разных городов недавно разговаривал о том, как (и почему именно так) нужно строить архитектуру при работе с веб-сервисами.
Поскольку сейчас я как раз занят тем, что пишу одновременно и слой веб-сервисов и клиента  к нему (с использованием того самого WPF), то об этом я и расскажу. Заодно прерву "затяжное молчание".

Итак, начнем есть трехслойный пирог (серверная логика, веб-сервисы, клиентская логика) с середины, то есть, со слоя веб-сервисов.

Создавая слой веб-сервисов мы создаем контракт между двумя остальными частями (серверной и клиентской). Сам же слой представляет собой набор доступных операций, этакий API, для слоя клиентской логики и является не более, чем клиентом для слоя логики серверной.
Подчеркну важный момент: в этой схеме "клиент" ничего не обязан знать о том, как там устроена серверная часть, какими данными она оперирует, в каком виде все это хранит и т.д. Клиентский слой логики "мыслит" совсем другими категориями, у него совершенно другие задачи и, быть может, даже другая бизнес-модель.

Отсюда вытекает простая рекомендация: не нужно передавать клиентскому приложению объекты бизнес-модели серверной логики. С точки зрения развития и поддержки приложения, я бы даже сказал "никогда не нужно".

Создавайте отдельные объекты для передачи посредством веб-сервисов. Такие объекты называются "контрактами данных" (Data Contracts).
Ваш слой веб-сервисов будет оперировать объектами бизнес-логики серверной части, но для передачи клиенту будет "транслировать" их и их данные в объекты-контракты данных и лишь затем передавать.

Зачем это нужно? Очень просто. Как я уже сказал, клиентское приложение, пользуясь веб-сервисом Reports, совершенно не заинтересовано "знать" бизнес-модель серверной части. И для него бизнес-сущность "User" вовсе не является набором "запись в таблицу Users + ссылка в таблицу Occupations + ссылка в таблицу Departments + 2 ссылки в таблицу Addresses по их IDшникам". Ему и надо-то воспользоваться сервисом Reports всего лишь для того, чтобы этот самый репорт отобразить, и для него User - это "монолитный" объект со всеми необходимыми адресами, должностями и т.д. А вот информация о паролях, логинах, ролях и т.д. ему совершенно не нужна, поэтому и передавать ее в сервисе Reports не за чем.
Поэтому слой веб-сервисов "берет" все эти серверные бизнес-объекты (адреса, должности), трансформирует эти данные в отдельный объект, определенный контрактом, и уже его передает клиенту.
Кроме этого, контракты данных получаются достаточно простыми и четкими, что позволяет легко использовать сервисы даже из не-.net приложений, если придется. Да и в любом случае удобнее, чем передавать сложные объекты бизнес-модели, часто имеющие свою иерархию, избыточную с т.з. веб-метода информацию и т.д.
Все счастливы.

- Счастливы в том числе и разработчики серверной логики, которые могут модифицировать объекты бизнес-логики, не боясь "сломать" интерфейс между сервером и клиентами.
- Счастливы разработчики самого слоя веб-сервисов которые могут сами решать, какие данные нужно передавать в сервисах и как, добавлять и удалять веб-службы и веб-методы, совершенно не беспокоясь о том, что в один прекрасный момент объекты серверной части поменяются (или объектов нужного типа просто нет) и они не смогут обеспечить необходимый формат данных.
- Счастливы разработчики клиентской части софта, так как они могут быть уверены, что для уже работающего функционала веб-методы вдруг не станут предоставлять "немного другие" другие данные.

Счастливы, в конце концов, те, кому все это потом поддерживать. Ибо в данном случае слои получаются слабо связанными, легко понимаемыми и легко модифицируемыми (в том числе и независимо друг от друга).

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

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

Второй: Вы сами контролируете объекты, с которыми вы работаете.
После получения данных от веб-сервиса и трансформации их в собственные объекты клиентской бизнес-модели, программисты оной могут творить с/из этих объектов свободно что угодно.
Нужно сделать Undo? Пожалуйста, добавили необходимые методы. Нужно, чтобы объект оповещал об изменении его свойств? Пожалуйста, реализовали INotifyPropertyChanged и получили беспроблемный двусторонний байндинг в UI. Нужна собственная иерархия? Опять пожалуйста, хозяин-барин.

Итак, коротко обобщим основные рекомендации:

  1. Создавайте на стороне серверной логики полноценные объекты бизнес-модели и работайте с ними (а не с именованными наборами данных, за поведение которых отвечают сторонние классы, которые хрен найдешь, если не знаешь).
  2. Создавайте контракты сервисов (API, наборы операций, веб-методы), которые передают и принимают контракты данных (простые типы-носители необходимых для операции данных). Никогда не используйте бизнес-объекты серверной логики в качестве контрактов данных.
  3. Не работайте напрямую с объектами-контрактами данных в клиентском приложении. Создайте полноценные объекты бизнес-модели клиентской части, и заполняйте их данными, полученными от веб-сервисов. Так вы одновременно получите возможность развивать и управлять этой моделью и избавитесь от необходимости реализовывать странные сторонние классы, отвечающие за манипуляцию данными (те самые, которые потом хрен найдешь и проконтроллируешь).

Словом, воспринимайте веб-сервисы, как средство передачи данных, а не объектов бизнес-модели. И будет вам простота разработки, и вы будете избавлены от необходимости поддерживать совершенно немодифицируемый (в силу очень сильной связанности всего и вся) код с разбросанным по разным местам поведением.

Пример (упрощенный) веб-метода, возвращающего данные о производителе товара:

public DataTypes.Brand GetByID(int id)
{
    BusinessLogic.ManufacturerGetById action = new BusinessLogic.ManufacturerGetById();
    BusinessEntities.Manufacturer m = action.Execute(id);
 
    return Translators.BrandToManufacturerTranslator.Translate(m);
}

Здесь ManufacturerGetById - это класс-action, умеющий вернуть сложный объект бизнес-модели серверной части по его идентификатору. Далее он трансформируется в простой объект контракта данных Brand, который и будет предоставлен клиенту.

Клиент же, получив эти данные, таким же способом заполняет ими свой объект
Brand : Company, INotifyPropertyChanged, ISearcheable
с которым и продолжает работать дальше.

Comments (1) -

5/17/2007 8:28:31 PM

Sergiy Oliynyk

Спасибо, интересный и очень полезный для меня пост.
Со многим полностью согласен. Смущает только необходимость несколько раз преобразовывать данные (объекты бизнес-модели серверной логики => контракты данных => бизнес-объекты клиентской модели).
Согласен, что преимущества такого подхода есть, но есть и недостатки - как минимум увеличение трудоемкости и накладные расходы при преобразовании данных.

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

Sergiy Oliynyk

Comments are closed

Powered by BlogEngine.NET 2.5.0.6

About the author

Alexey Raga Alexey Raga
.NET software developer.

E-mail me Send mail

Twitter

Widget Twitter not found.

Root element is missing.X


Recent posts

Archive

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2012

Sign in