Всем привет! Я часто ищу в Интернете «идеальную архитектуру», и несколько месяцев назад я наткнулся на интересную реализацию и хотел бы поделиться ею немного дальше.
→ Ссылка на реализацию Немного модернизации и я получил полностью универсальный рабочий шаблон.
Для тех, кто не знаком с DDD, вы можете начать с вики .
В итоге мы получим комбинацию DDD + CQRS + Entity Framework + OData + WebApi + Log4Net + Castle Windsor + Kendo UI. Звучит громоздко, но чисто лично для меня в результате получается достаточно легко масштабируемая система.
Конечный результат будет примерно таким Кликабельное изображение (на весь экран)
Итак, начнем…
Создайте папку «Домен и инфраструктура».
В папке Domain создаем 3 проекта (библиотеку классов):
- Домен.
Команды
- Домен.
База данных
- Домен.
Модель
- Инфраструктура.
Веб
- Инфраструктура.
Домен
- Инфраструктура.
EntityFramework
- Инфраструктура.
Легирование
И последний проект (библиотека классов) Веб приложение .
А теперь о каждом более подробно: CQRS (разделение ответственности за командный запрос) Немного о командах и запросах Запросы : методы возвращают результат без изменения состояния объекта.
Другими словами, Query не имеет никаких побочных эффектов.
Команды : методы изменяют состояние объекта, не возвращая значения.
На самом деле эти методы правильнее называть модификаторами или мутаторами, но исторически они назывались командами.
В проекте Domain.Commands мы будем хранить команды, которые будут менять состояние объекта и нашу бизнес-логику.
Вот что у нас будет Команда .
И мы будем использовать OData в качестве запроса.
В проекте Command.Database мы будем хранить схему базы данных (я обычно использую для этого PowerDesigner) и Seed-скрипты.
Мы храним все сущности в проекте Domain.Model. Теперь папка Инфраструктура.
Infrastrcuture.Domain — храним все помощники домена, построители команд, исключения, которые понадобятся для модели Домена.
Infrastrcuture.EntityFramework — это наша ORM. Infrastrcuture.Logging — ведение журнала.
Infrastrcuture.Web — веб-помощники, расширения, обработчики форм.
В проекте Web.Application. Создайте базовый класс для чтения (OData): ReadODataControllerBase.cs
И базовый контроллер формы: Формконтроллербасе.namespace Web.Application { using System.Linq; using System.Web.Http.OData; using Infrastructure.Domain; using Infrastructure.EntityFramework; public class ReadODataControllerBase<TEntity> : ODataController where TEntity : class, IEntity { private readonly IRepository<TEntity> _repository; public ReadODataControllerBase(IRepository<TEntity> repository) { _repository = repository; } public IQueryable<TEntity> Get() { return _repository.Query(); } } }
cs namespace Web.Application
{
using System;
using System.Net;
using System.Web.Mvc;
using Castle.Core.Logging;
using Castle.Windsor;
using Infrastrcuture.Web.Forms;
using Infrastructure.Domain.Exceptions;
using Infrastructure.EntityFramework;
using Services.Account;
using Services.Account.Models;
public class FormControllerBase : Controller, ICurrentUserAccessor
{
public JsonResult Form<TForm>(TForm form)
where TForm : IForm
{
var formHanlderFactory = ResolveFormHandlerFactory();
var unitOfWork = ResolveUnitOfWork();
var logger = ResolveLogger();
try
{
logger.Info($"Begin request of <{CurrentUser.DisplayNameWithNk}> with form <{ form.GetType().
Name }>.
"); formHanlderFactory.Create<TForm>().
Execute(form); unitOfWork.SaveChanges(); logger.Info($"Complete request of <{CurrentUser.DisplayNameWithNk}> with form <{ form.GetType().
Name }>.
"); return Json(new { form }); } catch (BusinessException be) { return JsonError(form, be, logger); } catch (FormHandlerException fhe) { return JsonError(form, fhe, logger); } catch (Exception e) { return JsonError(form, e, logger); } } //Add exception logging public FileResult FileForm<TForm>(TForm form) where TForm : IFileForm { var formHanlderFactory = ResolveFormHandlerFactory(); formHanlderFactory.Create<TForm>().
Execute(form); return File(form.FileContent, System.Net.Mime.MediaTypeNames.Application.Octet, form.FileName); } private JsonResult JsonError<TForm>(TForm form, Exception e, ILogger logger) { logger.Error($"Rollback request of <{CurrentUser.DisplayNameWithNk}> with form <{ form.GetType().
Name }>.
", e); Response.TrySkipIisCustomErrors = true; Response.StatusCode = (int)HttpStatusCode.InternalServerError; return Json(new { form, exceptionMessage = e.Message }); } #region Dependency resolution private IFormHandlerFactory ResolveFormHandlerFactory() { return GetContainer().
Resolve<IFormHandlerFactory>(); } private IUnitOfWork ResolveUnitOfWork() { return GetContainer().
Resolve<IUnitOfWork>(); } private ILogger ResolveLogger() { return GetContainer().
Resolve<ILogger>(); } private IWindsorContainer GetContainer() { var containerAccessor = HttpContext.ApplicationInstance as IContainerAccessor; return containerAccessor.Container; } private ICurrentUserKeeper ResolveCurrentUserKeeper() { return GetContainer().
Resolve<ICurrentUserKeeper>();
}
#endregion
#region CurrentUserAccessor Memebers
public ApplicationUser CurrentUser
{
get
{
var currentUserKeeper = ResolveCurrentUserKeeper();
return currentUserKeeper.GetCurrentUser();
}
}
#endregion
}
}
В итоге для чтения данных из базы мы просто создаем класс и наследуем его от класса ReadODataController и просто переходим в локальный хост :12345/odata/Станции.
Весь запрос записывает для нас OData: СтанцииController.cs
namespace Web.Application.Station
{
using Domain.Model.Station;
using Infrastructure.EntityFramework;
public class StationsController : ReadODataControllerBase<Station>
{
public StationsController(IRepository<Station> repository) : base(repository)
{
}
}
}
Одатаконфиг.
cs Одатаконфиг.
cs
namespace Web
{
using System.Linq;
using System.Web.Http;
using System.Web.Http.OData.Builder;
using System.Web.Http.OData.Extensions;
using Domain.Model.Station;
using Infrastrcuture.Web.Extensions;
using Microsoft.Data.Edm;
public class ODataConfig
{
public static void Register(HttpConfiguration config)
{
var builder = new ODataConventionModelBuilder();
config.Routes.MapODataServiceRoute("odata", "odata", GetEdmModel(builder));
}
public static IEdmModel GetEdmModel(ODataConventionModelBuilder builder)
{
var entityTypes = typeof (Station).
Assembly.GetTypes().
Where(x => x.IsClass && !x.IsNested); var method = builder.GetType().
GetMethod("EntitySet");
foreach (var entityType in entityTypes)
{
var genericMethod = method.MakeGenericMethod(entityType);
genericMethod.Invoke(builder, new object[]
{
entityType.Name.Pluralize()
});
}
return builder.GetEdmModel();
}
}
}
Этот шаблон уже был протестирован и сейчас один из наших проектов нормально работает в продакшене, без каких-либо проблем.
Ссылка на проект: NTemplate Теги: #C++ #.
NET #ddd #cqrs #Виндзорский замок #.
NET #C++
-
Обзор Hp Probook 4520S-Wd843Ea
19 Oct, 24 -
Портативные Наушники Igrado
19 Oct, 24 -
It-Радио «Пром.работка»: Запись Эфира №1
19 Oct, 24