Структура моих SharePoint проектов
Структуру своего первого SharePoint проекта я строил, руководствуясь принципами и практиками, которые я выработал, будучи ASP.NET разработчиком. Модульность, слабая связанность между частями проекта, точки агрегации зависимостей – все эти принципы так же удачно ложатся в основу структуры SharePoint проекта, как и в структуру большинства проектов созданных на основании других технологий. Если для вас эта тема нова или же хотите ее освежить, то я настоятельно рекомендую вам ознакомиться с серией статей Simon Ince посвященных созданию архитектуры на базе принципов заложенных в Microsoft Web Client Software Factory. В свое время они перевернули мое представление о том, как надо структурировать проекты.
Моя типичная структура одного модуля в ASP.NET проекте с backend-ом, скажем, в виде MS SQL базы данных выглядела следующим образом:

Думаю, что никого не удивил этой картинкой. Все как обычно: Repository формируется с учетом особенности базы данных, View завязывается на модель представления предлагаемую ASP.NET-ом, ну а Services (они же BLL) опираются лишь на интерфейсы торчащие из DAL и PL и ни на что конкретное не завязываются. В случае с ASP.NET такая архитектура, в принципе, разворачивается без особых проблем – пара-тройка сервисов-фасадов для работы с HttpContext.Current и у нас полностью развязаны руки в BLL.
Но при попытке построить архитектуру SharePoint проекта по этим же принципам возникают некоторые трудности
В итоге, вместо, примерно, такой структуры:

У меня получилось вот это:

Иными словами: все слои приложения завязываются на модель SharePoint. При этом мы, разумеется, теряем всякую гибкость и жестко привязываемся всеми слоями к сущностям SharePoint. Жуть, не правда ли? Я долго и тщетно пытался все-таки скрыть от Services тот факт, что он работает с SharePoint. Но многочисленные фасады, адаптеры, прокси и прочие паттерны, которые я пытался применять, сильно замедляли процесс разработки и, в конечном счете, настолько усложняли код, что это делало его практически нечитаемым. Сейчас я остановился на мысли, что отвязаться от сущностей SharePoint на BLL не удастся, т.к. большая часть логики определяется структурой SharePoint и выполнения большей части операций приходится оперировать именно ими, а не Domain Model объектами, как это было в решениях, построенных на более легковесных платформах.
Ну, с архитектурными принципами, вроде бы более или менее определились, теперь, давайте более подробно остановимся на внутренней структуре моего текущего SharePoint проекта. Сразу оговорюсь – проект не очень большой – всего 3-4 модуля и 2 уровня их иерархии. Поэтому некоторыми из принципов я пренебрег в угоду простоты разработки и компактности решения. Вот, что у меня получилось:

Весь проект разбит на модули в соответствии с их назначением. Так, к примеру, у меня были модули для работы с поручениями, жалобами, предложениями, организациями и модуль для шаблона сайта. Такое модульное разделение позволяет жестче контролировать зависимости между сервисами, т.к. каждая зависимость выливается в reference между проектами. К тому же мы получаем возможность развертывать и отлаживать каждый модуль по отдельности.
Далее, каждый из модулей в свою очередь состоит из нескольких проектов. Рассмотрим назначение каждого из проектов на примере модуля жалоб.

- Complaints – SharePoint проект с представлениями. Здесь у нас находятся Controls, Application Pages, WebParts, EventReceivers(сами определения, но не их логика), Ribbons. Плюс здесь находятся Presenter-ы (я использую именно этот паттерн для разделения логики и представления). В процессе разработки этот модуль deploy-ится чаще всего.
- Complaints.Lists – SharePoint проект содержащий все, что связанно с уровнем хранения: Fields, Content Types, List Definitions и Instances. Обычно, заполняем его в самом начале и далее уже не трогаем.
- Complaints.Manager – самая обычная .NET сборка, deploy которой выполняется через обычное копирование в GAC (включена в пакет всех проектов, которые ее используют). По сути, это наша рабочая лошадка. Здесь находятся репозитории для работы со списками, сервисы с бизнес логикой, утилиты и всякие вспомогательные сущности.
- Complaints.Workflow – Ещё один SharePoint проект с рабочими процессами.
С назначением проектов в модуле вроде бы разобрались. Теперь перейдем к внутреннему строению каждого из модулей.
Common

В основе решения лежат 4 сборки с общими компонентами:
- Common.References – пустой проект, который разворачивает все сторонние сборки в GAC.
- Common.UI – проект с общими страницами и эл-тами управления (№ custom страница с сообщением об ошибке).
- Framework – проект с различными вспомогательными утилитами и базовыми классами, которые не относятся к предметной области.
- GlobalResources – ну, думаю, это из названия понятно.
Complaints

В принципе, тут все должно быть понятно из скриншота. Все четко и по полочкам. В Pages лежат строковые константы с путями и аргументами страниц использующихся внутри модуля. В Views – реализации паттерна MVP.
Complaints.Lists

Все сгруппировано по спискам. Там где есть необходимость – определены Custom формы. Они также построены с использованием паттерна MVP. Field-ы лежат в корне проекта.
Complaints.Manager

Пожалуй, самый интересный и сложный по своей структуре модуль.
- В Entities лежат различные вспомогательные DTO, которые, как правило, формируются в сервисах и передаются во View.
- Handlers – это своеобразные Presenter-ы для EventReceiver-ов. Они определяют какие сервисы и в каком порядке вызывать для обработки того или иного события.
- С Helpers, Resources и Services, думаю все понятно.
- Для каждого из списков имеется свой Repository, в который включены следующие файлы:
- Entity – небольшой и абсолютно «плоский» DTO, который формируется вручную из SPListItem. Используется там, где-то нужно просто получить и отобразить.
- Fields – набор строковых констант-имен полей.
- ListDefinition – сущность, отображающая сам список. Содержит ряд вспомогательных методов для работы с ним (но не с его элементами).
- Repository – а это, непосредственно, сам репозиторий с CRUD методами для работы с элементами списка. Он абсолютно stateless, все, что нужно – приходит к нему в аргументах методов.
Модуль может зависеть только от других Manager-ов. Но это крайне нежелательно. В идеале – он автономен и зависит лишь от утилитарных сборок, вроде GlobalResources и Framework.
Complaint.Workflow

Тут ничего особенного. Просто определения рабочих процессов, из которых дергаются необходимые сервисы, определенные в ComplaintsManager.
Вот, что у нас в итоге получается в плане зависимостей между проектами модулями:

А вот схема зависимостей между модулями:

Архитектура не идеальная, но вполне жизнеспособная и расширяемая. При дальнейшем росте проекта и увеличении степени зацепления между модулями имеет смысл выделить интерфейсы сервисов и сущностей в отдельную сборку. Но тут надо быть аккуратным и не плодить сильно много SharePoint проектов – иначе замучаетесь ждать пока выполнится их deploy. Тему архитектуры проектов в SharePoint можно развивать и дальше, но, пожалуй, оставлю это на следующую статью. А сейчас жду ваших комментариев и предложений!
Очень полезная статья. Спасибо.
Если возможно, было бы интересно узнать про реализации паттерна MVP на примере.
Что содержится в Search.aspx, ISearchView.cs, SearchPresenter.cs?
akornev, пример с Search не очень нагляден, т.ч. давайте я приведу пример эл-та управления для работы с рейтингом реализованным с использованием паттерна MVP.
Вот, непосредственно, код: http://codepaste.net/pm164d (на небольшие опечатки не обращайте внимание, вырезал мусор).
Думаю, все должно быть более или менее понятно:
View знает о Presenter и при необходимости дергает его методы, передавая туда весь нужный контекст.
Presenter ничего не знает о конкретных View и оперирует лишь интерфейсом IView, через которое получает доступ к его свойствам и, возможно, методам (но это реже).
Если что, хорошая статья про паттерн MVP есть на rsdn: http://www.rsdn.ru/article/patterns/ModelViewPresenter.xml
+ Советую обратить внимание на мою беседу с Андреем Макеевым на тему рациональности использования MVP в SharePoint: https://plus.google.com/111626777246307386943/posts/WC2DQ9mwnaL