"наследование" сайтов ASP.NET MVC из общего приложения шаблонов? (мульти аренды)
Мы создаем около 10 сайтов ASP.NET MVC, которые имеют общий набор функций (и соответствующие URL-адреса, маршруты, контроллеры, действия и представления). На всех сайтах также будет использоваться базовый набор объектов домена (например, пользователей, компаний) и базовые атрибуты для этих объектов (например, имя, адрес и т.д.).
Но каждый сайт также будет сильно настроен и расширен от базы. Например, наш сайт для крупных публичных компаний будет иметь поля "Вспомогательный" и "Фондовый символ" на объекте домена компании, а наш сайт для стартапов будет иметь атрибуты "Венчурная фирма" и "Финансирование". Внешний вид также будет значительно отличаться, хотя мы стараемся, чтобы HTML был максимально согласован (по модулю дополнительных полей формы для дополнительных атрибутов объекта домена и т.д.). Мы также будем отсеивать изображения экономно, поэтому мы можем, например, повторно использовать одну и ту же графику кнопок на сайтах.
В любом случае мы пытаемся выяснить, как лучше всего учитывать факторы и архитектуру, чтобы мы могли повторно использовать как можно больше кода и как можно больше тестов без ограничения нашей свободы добавлять атрибуты для каждого приложения и изменять пользовательский интерфейс между приложениями.
Я знаком с тем, как обрабатывать многоуровневую ограниченную настройку, как вы находите в StackOverflow/SuperUser/ServerFault (или MSDN/TechNet, если на то пошло), где пользовательский интерфейс немного отличается, а модель данных - или менее идентичны. Но когда модели и пользовательский интерфейс сильно отличаются (но наследуются от общей базы), я не уверен, как действовать дальше.
Меня меньше беспокоят операционные проблемы, так как мы, вероятно, будем запускать каждый сайт в отдельном приложении и размещать их в отдельных базах данных. Я больше беспокоюсь о снижении долгосрочных расходов на обслуживание кода, повышении гибкости (например, легко добавлять новые функции в базу без разрыва производных приложений) и реализовать краткосрочную экономию средств на основе dev/test-cost, поскольку мы создаем наши 2-й, 3-й, 4-й и т.д. Сайт.
Я ищу как для руководства и предложений высокого уровня, так и для конкретных предложений о том, как сделать это руководство реальным, используя современные методы ASP.NET MVC.
Я понимаю, что это очень общий вопрос, но для начинающих я ищу как руководство высокого уровня, так и конкретные советы - n-трюки для того, как применять это руководство с ASP.NET MVC, включая такие вещи, как:
- рекомендации по разделению базы/производных в проектах Visual Studio
- наконечники управления источником, чтобы избежать разветвления
- подсказки схемы базы данных (FWIW, наши базы данных невелики - под 10K строк на таблицу, поэтому стоимость dev/test больше связана с проблемой, чем DB perf)
- советы по повторному использованию контроллеров /Views/etc. соответствующие "базовым" характеристикам модели, особенно повторное использование пользовательского интерфейса для таких вещей, как формы "нового клиента", которые будут содержать базовые и производные атрибуты.
У кого-нибудь есть хороший совет для того, как создать многопользовательское приложение вроде этого?
Ответы
Ответ 1
Вот что мы делаем, и это работает очень хорошо для примерно 8 сайтов в настоящее время.
-
Определите основной проект MVC для ваших контроллеров, ViewModels, HttpApplication, маршрутов и т.д. Это скомпилируется в DLL и скомпрометирует основную часть вашего сайта.
-
Создайте базовый набор представлений по умолчанию, сценариев, изображений и т.д. для вашего сайта. Они будут использоваться как по умолчанию для ваших отдельных сайтов.
-
На клиент, создайте любые настраиваемые контроллеры, маршруты и т.д., которые вам понадобятся в проекте, который компилируется в другую dll.
-
Также для каждого клиента заново создайте все виды, сценарии, изображения, которые вы хотите использовать.
Чтобы эти шаги работали вместе, вам нужно написать небольшой клей. Первый кусок клея - это нестандартный механизм просмотра. Вы хотите настроить стандартный механизм просмотра, чтобы сначала просмотреть представления в папке, специфичной для клиента, а затем папку по умолчанию. Это позволяет легко переопределить компоновку по умолчанию для каждого клиента.
Второй способ получения всего рабочего - заставить ваше основное приложение загружать маршруты, контроллеры и т.д. из вашей конкретной сборки вашего клиента. Для этого я использую Managed Extensibility Framework (MEF), чтобы разоблачить один метод Register. Вызов этого метода на моем коде ассемблера клиента регистрирует маршруты и любые другие специфические для клиента потребности.
Вот общий вид структуры структуры моего сайта, при этом сначала проверяется SiteContent для просмотров:
- AppContent
- AppContent/Static
- AppContent/Static/Images
- AppContent/Static/Scripts
- AppContent/Static/Styles
- AppContent/Views
- AppContent/Views/Shared
- SiteContent
- SiteContent/Static
- SiteContent/Static/Images
- SiteContent/Static/Scripts
- SiteContent/Static/Styles
- SiteContent/Views
- SiteContent/Views/Shared
- web.config
- Global.asax
У меня есть помощники, которые я могу использовать как SiteImage и AppImage для использования в моих представлениях. Кроме того, я делаю, чтобы каждый из моих клиентских сайтов использовал определенные имена для своих основных страниц, которые я никогда не определяю в своих настройках по умолчанию для AppContent.
Я понимаю, что это общий обзор, но он работает достаточно хорошо для нас прямо сейчас.
Ответ 2
Я участвую в подобном "наборе" проектов, которые в настоящее время сосредоточены на предоставлении клиентам возможности обращаться к продуктам в Интернете, но имеют очень схожие требования к тому, какую информацию следует собирать, где единственные различия касаются отдельных частей продукта информацию или несколько иные законодательные требования.
Одна вещь, которую мы пытались сделать, - создать страницы (модели, представления и комбинации контроллеров), которые могут использоваться повторно, поэтому любое приложение может использовать страницу для захвата информации, но перенаправлять на следующую страницу, которая может быть различной в зависимости от для какого типа продукта применяется. Для этого мы используем абстрактные базовые контроллеры в виде шаблона шаблона метода, которые содержат в основном всю необходимую логику контроллера (включая методы действий с их применяемыми фильтрами действий), но затем используют абстрактные методы для выполнения конкретных действий, таких как перенаправление на следующая страница в этом процессе. Это означает, что конкретная реализация контроллера, используемого конкретными потоками страниц приложения, может содержать только один метод, который возвращает RedirectToActionResult, соответствующий следующей странице в потоке.
Существует также немало других вещей, которые обрабатывают обратное движение и эти виды навигационных вещей, но с помощью фильтров действий вы можете настроить его, чтобы вам не пришлось беспокоиться об этом, как только вы его заработаете и работаете,
Существуют также объекты базовых моделей, которые содержат общую функциональность, будь то логика проверки или логика сохранения состояния.
Данные, записанные во время процесса приложения, сохраняются в базе данных как объекты сериализованной модели xml, которые затем могут быть вытащены и де-сериализованы после завершения приложения и выплескиваются в любом формате в любую систему, используемую персоналом операционной системы для обработки приложения.
Это связано с тем, что мы имеем структуру проекта, состоящую из базовой dll, которая содержит абстрактные классы верхнего уровня, интерфейсы и классы полезности, а также html-помощники, фильтры действий и т.д. Затем у нас есть проекты mvc, которые содержат конкретные реализации базовых контроллеров, моделей и т.д., а также представлений и мастер-страниц.
Самое сложное - делиться мнениями, и я не думаю, что мы все еще правильно разобрались. Хотя с MVC 2.0, содержащим Areas, я думаю, что это станет проблемой, но пока у меня не было хорошей игры. (см. сообщение Скотта Гу на 2.0: http://weblogs.asp.net/scottgu/archive/2009/07/31/asp-net-mvc-v2-preview-1-released.aspx)
У меня есть POCed, похоже, что она будет работать, используя базовый проект MVC, чтобы содержать общие представления, а затем расширять механизм просмотра по умолчанию для поиска этого проекта на веб-сервере при поиске вида для рендеринга (что довольно легко сделать). Области, хотя это гораздо более приятное решение.
Что касается управления версиями, мы используем svn, и я думаю, что вы разумны в отношении ветвей. Это не то, с чем нам пришлось иметь дело, но мы, вероятно, собираемся пойти с git, поскольку он, кажется, делает процесс ветвления и слияния гораздо менее болезненным.
Не уверен, что это вам очень помогает, но я бы определенно рекомендовал иметь в виду абстрактные контроллеры и модели, а также посмотреть, как вы можете использовать html-помощники и частичные представления для группировки похожих функций.
Ответ 3
Майк Хэдлоу подробно описывает, как это сделать:
http://mikehadlow.blogspot.com/2008/11/multi-tenancy-part-1-strategy.html
Ответ 4
Один из способов сделать это - использовать ветвление в исходной системе управления.
Основная ветвь предназначена для общей функциональности. Затем у вас есть ветка для настройки и вы можете объединить изменения в настройку или вернуться к основной ветке.