Friday, September 12, 2008

NHibernate 2.o и Linq.

Недавно вышел релиз NHibernate 2.0 (сейчас ожидается sp1). К сожалению, в данной версии отсутствует официальная поддержка Linq. На данный момент существует экспериментальная реализация Linq for NHibernate. Инициирована она была одним из разработчиков NHibernate Oren Eini и находилась в его репозитории, потом была перенесена в репозиторий NHibernate Contrib. Реализация основана на NHibernate 2.0 Beta2 (Daniel Guenter реализовал патч, позволяющий работать ей не с бета-версий, а с релизом), имеет достаточное количество недоработок и дефектов, поэтому использовать ее в реальных проектах пока не рекомендуется. Допустим, этот простой запрос не будет работать:

[Category("JOIN")]
[Test]
[Description("This sample uses foreign key navigation in the " +
"from clause to select all orders for customers. Not actual.")]
public void DLinqJoin1c()
{
var q =
from c in db.Customers
from o in c.Orders.Cast<Order>()
where o.OrderID=100
select c;

var list = q.ToList();

ObjectDumper.Write(q);
}

Так же, в этих же тестах очень много [Ignore("TODO")].

Полную же поддержку Linq for NHibernate, разработчики обещают в NHibernate 2.1. Сейчас разработка Linq for NHibernate перенесена в репозиторий(trunk) NHibernate, поскольку полная реализация требует незначительных изменений последнего. Из моей переписки с одиним из разработчиков Linq for NHibernate я выяснил, что теперь избран путь генерации SQL при обработке выражений(expression tree), вместо использования Criteria API(как это делается в экспериментальной реализации). Будем следить за изменениями и ждать выхода версии 2.1.

Thursday, September 11, 2008

Сервисы в Domain-Driven дизайне.

В предметной области (домене) встречаются операции, которые не являются частью сущностей (Entity) или простых объектов-значений (Value Objects). Некоторые из них, по сути, представляют некоторое действие или деятельность, но следуя парадигме Domain Driven Design (DDD), где все является объектами, эти действия необходимо представить как объекты. Распространенной ошибкой является вынесение логики (поведения) из сущностей и объектов-значений, и помещение ее в классы сервисов. Сложные операции, в итоге, переполняют объект, искажая его первоначальную роль, поэтому операция в сервисе должна объединять и координировать логику из других доменных объектов, связывая объекты таким образом, что бы представляемые ими концепции, могли рассматриваться отдельно. В отличие от сущностей и объектов-значений сервисы объявлены в терминах того, что они могут сделать для клиента. Имена сервисов строятся на основании деятельности, которую они представляют, в отличие от сущностей.

Основными характеристиками хорошего сервиса являются:

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

Примеры сервисов домена: IOrderProcessor, IProductFinder, IFundsTransferService.

Поскольку сервис распространенный технический паттерн, помимо доменного слоя он может находиться в других логических слоях приложения. Типы сервисов:

  • cервисы приложения(Application);
  • сервисы предметной область(Domain);
  • инфраструктурные сервисы(Infrastructure).

Инфраструктурные сервисы, это что взаимодействует непосредственно с внешними ресурсами, например файловой системой, базой данных, реестром, сетью и т.п. Примером инфраструктурного сервиса может служить: IEmailSender.

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

Рассмотрим пример разбиения сервисов по слоям:


Application:
Funds Transfer App Service
- обрабатывает входные данные (например запрос в формате XML);
- обращается к сервису домена для осуществления перевода;
- ждет подтверждения;
- решает посылать ли нотификацию через инфраструктурный сервис.

Domain:
Funds Transfer Domain Service
- взаимодействует с необходимыми объектами Account и Ledger, вызывая соответствующую логику предметной области(домена);
- предоставляет результат (разрешен ли перевод или нет и т.п.).

Infrastructure:
Send Notification Service
- посылает e-mail'ы, факсы и осуществляет другую нотификацию поддерживаемую приложением.

Литература: "Domain-Driven Design: Tackling Complexity in the Heart of Software" by Eric Evans.

Monday, September 8, 2008

Почему я не использовал модульное тестирование 3 года назад.

Для тех из нас, кто открыл для себя модульное тестирование (Unit Testing) очень сложно понять, почему до сих пор не все используют его. У тех, кто еще не познал его, есть сокровенное желание, что бы те, кто познал, перестали постоянно и занудно сверлить им в мозг этим самым модульным тестированием. На протяжении многих лет, я читал блоги и статьи о модульном тестировании, общался с коллегами, которые его эффективно использовали(правда их было не очень много), но сам не использовал его в своей работе. Оглядываясь назад, можно выделить следующие причины:

  1. Я был в заблуждении относительно выгоды использования модульного тестирования. Модульное тестирование повышает качество вашего приложения, что на самом деле в дальнейшем так же упрощает модифицируемость и поддержу приложения. В будущем, если продвинуться на следующую ступень и освоить Test Driven Development (разработку основанную на тестировании:)), модульное тестирование становится неотъемлемой частью процесса формирования дизайна (детальной архитектуры) вашего приложения. "TDD это не тестирование, потому что вы не думаете как тестер, когда занимаетесь им, вы думаете как дизайнер." - Scott Bellware.
  2. Как и многие другие, я привык думать, что тестирование это проблема QA инженеров, но, ни как не моя. Я не знаю, откуда была такая уверенность, но сейчас я убежден, что это отговорка неквалифицированных разработчиков. Тестирование - это процесс нахождения дефектов (багов) в приложении, но это так же, в равной степени, и процесс проверки написанного (кода) на то, что оно работает как и задумывалось. Может быть разработчику и сложно найти дефект в написанном им приложении, но кто лучше него может убедится в том, что написанный им код, делает именно то, что он хотел?
  3. Тестирование это не особо весело. Сидеть перед монитором, вбивать входные данные и проверять, что все работает как и задумывалось, полный "ацтой". Но написание модульных тестов это тоже программирование, которое ничем не отличается от работы выполняемой нами каждый день. Временами, как и в разработке других частей приложения, это не совсем гениальный код, но при его написании совершенствуются имеющиеся навыки работы и осваиваются новые подходы и техники.
  4. Написание тестов занимает время. На самом деле модульное тестирование не тратит ваше время, оно экономит его. Время, затрачиваемое на проверку после внесения изменений и на исправление дефектов намного превышает время написания модульных тестов. Если честно, написание и модификация тестов действительно отнимает время (особенно много, когда вы только начинаете использовать модульное тестирование). У вас может не хватать времени на написание модульных тестов или ваш клиент(руководство) могут не понимать, что эти временные затраты оправданы, в таких случаях необходимо выделить критически важные секции кода вашего приложения, и производить, по возможности, модульное тестирование на них.

Модульное тестирование выглядело сложной и загадочной вещью применяемой только в ряде крайних случаев. Все его достоинства казались недостижимыми, а сроки на корню обрубали возможность его использования. Оказалось, что это требует достаточно много практики, но выгоды от использования модульного тестирования становились незамедлительно видны.

Литература: "Foundations of Programming: Building Better Software" by Karl Seguin.