Модульная структура –
быть или не быть?
Денис Цветцих
АстроСофт
http://www.astrosoft.ru/
D2D Just.NET
13 февраля 2016
dev2dev.ru
2
Кто я?
• 7 лет .NET
• Разработка корпоративных приложений
• Паттерны, архитектура
• Power Tools
3
Проекты для нескольких заказчиков
• Биллинговая система
• Внедрена 20-30 заказчикам с доработками
• MES-система
• Внедрена 150 заказчикам, для 10 есть доработки
В обоих случаях доработки для заказчиков
стали самостоятельными проектами
4
Как все начиналось
Как-то так
Мы уже продали систему другому заказчику!
Но нужно чуть-чуть доработать. И быстро!
Или так
Мы напишем проект для заказчика
А потом сделаем из него продукт!
И заканчивается одинаково
Нужно поддерживать несколько версий проекта
Кому знакомо?
5
Что делать – вроде бы понятно
• Разделить проект на части (модули)
• Для разных заказчиков
• повторно использовать общие модули
• реализовать специфичные модули
6
Как это сделать – непонятно
• Как устроен проект, позволяющий
повторно использовать свои модули?
• Как делить функционал на модули?
• Как собрать проект из модулей?
КАК КОНСТРУИРОВАТЬ ПРОЕКТ
8
Проект для одного заказчика
UI
Logic
DataAccess
Нельзя повторно использовать части, так как
они зависят от нижних уровней
9
Inversion of Control
• Модули верхнего уровня не должны
зависеть от модулей нижнего уровня. И
те, и другие должны зависеть от
абстракции.
• Абстракции не должны зависеть от
деталей. Детали должны зависеть от
абстракций.
10
IoC проект
UI
Logic
DataAccess interfaces
Можно повторно использовать части системы
Logic interfaces
DataAccess
ДЕКОМПОЗИЦИЯ
12
Модуль
Модуль – компонент программы, имеющий
• назначение
• контракт (реализуемые и используемые
сервисы)
Что такое модуль в вашей системе –
решаете сами 
13
Какие бывают модули
Пакет (Nuget)
одна или несколько сборок
может зависеть от других модулей
Плагин
одна или несколько сборок
не зависит от других модулей
14
Модульное приложение
Состоит из:
• Оболочка (Shell)
• Инфраструктура
• Загрузка модулей
• Модули
15
Горизонтальная декомпозиция (пакеты)
Shell (Интернет-магазин)
UI модуль
UI личного
кабинета
Logic модуль
UI корзины
Оформление
заказа
Изменение
настроек
Data Access модуль
Сохранение
настроек
Сохранение
заказа
16
Когда использовать
• Один проект, много заказчиков
• Не нужна новая реализация всех сервисов
• Кастомные модули конфигурируют и
переопределяют часть базового функционала
17
Вертикальная декомпозиция (плагины)
Shell (Visual Studio)
Модуль (плагин)
UI services
Logic services
DataAccess
services
Resharper (плагин)
UI формы
Рефакторинги
Сохранение
настроек
18
Когда использовать
• Система, состоящая из подсистем
• Подсистема – это плагин
• Линейка взаимодополняющих продуктов
• В оболочке один плагин – один продукт
• Если плагинов >= 3, тогда profit 
• Проектировать плагины нужно сразу 
ЗАГРУЗКА
20
Composition Root
Composition root – единственная точка в
приложении, где определяется какие
реализации соответствуют каким
интерфейсам
Располагается в Shell рядом со входом:
• Global.asax
• App.xaml.cs
• Инфраструктура (Application, Bootstrapper)
21
Composition Root без модулей
protected override void OnStartup(StartupEventArgs e)
{
var builder = new ContainerBuilder();
// Data Access service
builder.RegisterType<Repository>().As<IRepository>();
// Business Logic service
builder.RegisterType<LogicService>().As<ILogicService>();
// User Interface service
builder.RegisterType<NavigationService>().As<INavigationService>();
var container = builder.Build();
}
22
Composition Root без модулей
protected override void OnStartup(StartupEventArgs e)
{
var builder = new ContainerBuilder();
// Data Access service
builder.RegisterType<Repository>().As<IRepository>();
// Business Logic service
builder.RegisterType<LogicService>().As<ILogicService>();
// User Interface service
builder.RegisterType<NavigationService>().As<INavigationService>();
var container = builder.Build();
}
23
Composition Root без модулей
protected override void OnStartup(StartupEventArgs e)
{
var builder = new ContainerBuilder();
// Data Access service
builder.RegisterType<Repository>().As<IRepository>();
// Business Logic service
builder.RegisterType<LogicService>().As<ILogicService>();
// User Interface service
builder.RegisterType<NavigationService>().As<INavigationService>();
var container = builder.Build();
}
24
Composition Root без модулей
protected override void OnStartup(StartupEventArgs e)
{
var builder = new ContainerBuilder();
// Data Access service
builder.RegisterType<Repository>().As<IRepository>();
// Business Logic service
builder.RegisterType<LogicService>().As<ILogicService>();
// User Interface service
builder.RegisterType<NavigationService>().As<INavigationService>();
var container = builder.Build();
}
25
Особенности
Чем плохо
• Непонятно, какие сервисы из какого модуля
• Нельзя инкапсулировать реализацию
сервисов
• Повторное использовние модуля – копипаст
регистрации сервисов в контейнере
Чем хорошо
• Модули не зависят от IoC контейнера
26
Composition Root с модулями
• В каждом модуле – специальный класс,
регистрирующий сервисы этого модуля
• В Composition Root – регистрация
модулей, а не сервисов
27
Варианты загрузки модулей
• Императивный
• Декларативный
28
Императивный
• Composition Root отдает модулю IoC
контейнер
• Модуль регистрирует в контейнере свои
сервисы
29
Autofac: модуль
public class AutofacModule : Module
{
protected override void Load(ContainerBuilder builder)
{
// Регистрируем сервисы
builder.RegisterType<Service>().As<IService>();
}
}
30
Autofac: модуль
public class AutofacModule : Module
{
protected override void Load(ContainerBuilder builder)
{
// Регистрируем сервисы
builder.RegisterType<Service>().As<IService>();
}
}
31
Autofac: Composition Root
private IContainer Container;
protected override void OnStartup(StartupEventArgs e)
{
var builder = new ContainerBuilder();
builder.RegisterModule<AutofacModule>();
Container = builder.Build();
}
32
Autofac: Composition Root
private IContainer Container;
protected override void OnStartup(StartupEventArgs e)
{
var builder = new ContainerBuilder();
builder.RegisterModule<AutofacModule>();
Container = builder.Build();
}
33
Autofac: Composition Root
private IContainer Container;
protected override void OnStartup(StartupEventArgs e)
{
var builder = new ContainerBuilder();
builder.RegisterModule<AutofacModule>();
Container = builder.Build();
}
34
Особенности императивного варианта
Достоинства
• Показывает сервисы модуля
• Инкапсуляция реализации сервисов
• Просто перейти на другой IoC контейнер
• Быстрый
Недостатки
• Не подходит для плагинов (небезопасный)
• Порядок загрузки модулей может быть важен
35
Декларативный
• В модуле определение сервисов при
помощи метаданных
• Атрибуты
• Реализация интерфейсов
• Composition Root анализирует
метаданные при помощи Reflection
36
MEF: модуль
public interface IService
{
}
[Export(typeof(IService))]
public class Service : IService
{
}
37
MEF: модуль
public interface IService
{
}
[Export(typeof(IService))]
public class Service : IService
{
}
38
MEF: Composition Root
var configuration = new ContainerConfiguration()
.WithAssembly(typeof (IService).Assembly);
var container = configuration.CreateContainer();
39
MEF: Composition Root
var configuration = new ContainerConfiguration()
.WithAssembly(typeof (IService).Assembly);
var container = configuration.CreateContainer();
40
Особенности декларативного варианта
Достоинства
• Позволяет писать плагины
• Порядок загрузки модулей произвольный
Недостатки
• Нельзя инкапсулировать реализации сервисов
• Нельзя явно увидеть список сервисов модуля
• Сложнее перейти на другой IoC контейнер
• Загрузка может быть долгой
41
Пример: интернет - магазин
Товары имеют вес и размеры (длина,
ширина, высота)
Корзина считает стоимость доставки товаров
Базовый сценарий: расчет исходя из объема
Кастомный сценарий: расчет исходя из веса
Система реализована по принципу IoC 
Нужно реализовать кастомный сценарий
42
Common.Domain
public class Product
{
public int Id { get; set; }
public int Height { get; set; }
public int Width { get; set; }
public int Length { get; set; }
public int Weight { get; set; }
}
43
Common.Domain
public class Product
{
public int Id { get; set; }
public int Height { get; set; }
public int Width { get; set; }
public int Length { get; set; }
public int Weight { get; set; }
}
44
Common.Logic - CostCalculator
public interface ICostCalculator
{
int GetDeliveryCost(Product product);
}
internal class VolumeCostCalculator : ICostCalculator
{
public int GetDeliveryCost(Product p)
{
return p.Height * p.Width * p.Length;
}
}
45
Common.Logic - CostCalculator
public interface ICostCalculator
{
int GetDeliveryCost(Product product);
}
internal class VolumeCostCalculator : ICostCalculator
{
public int GetDeliveryCost(Product p)
{
return p.Height * p.Width * p.Length;
}
}
46
Common.Logic - CostCalculator
public interface ICostCalculator
{
int GetDeliveryCost(Product product);
}
internal class VolumeCostCalculator : ICostCalculator
{
public int GetDeliveryCost(Product p)
{
return p.Height * p.Width * p.Length;
}
}
47
Common.Logic - CostCalculator
public interface ICostCalculator
{
int GetDeliveryCost(Product product);
}
internal class VolumeCostCalculator : ICostCalculator
{
public int GetDeliveryCost(Product p)
{
return p.Height * p.Width * p.Length;
}
}
48
Common.Logic - ShopingCart
internal class ShopingCart : IShopingCart
{
private ICostCalculator _calculator;
private List<Product> _products = new List<Product>();
public ShopingCart(ICostCalculator calculator)
{
_calculator = costCalculator;
}
public int GetDeliveryCost()
{
return _products
.Sum(p => _calculator.GetDeliveryCost(p));
}
}
49
Common.Logic - ShopingCart
internal class ShopingCart : IShopingCart
{
private ICostCalculator _calculator;
private List<Product> _products = new List<Product>();
public ShopingCart(ICostCalculator calculator)
{
_calculator = costCalculator;
}
public int GetDeliveryCost()
{
return _products
.Sum(p => _calculator.GetDeliveryCost(p));
}
}
50
Common.Logic - ShopingCart
internal class ShopingCart : IShopingCart
{
private ICostCalculator _calculator;
private List<Product> _products = new List<Product>();
public ShopingCart(ICostCalculator calculator)
{
_calculator = costCalculator;
}
public int GetDeliveryCost()
{
return _products
.Sum(p => _calculator.GetDeliveryCost(p));
}
}
51
CommonLogicModule
public class CommonLogicModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder
.RegisterType<VolumeCostCalculator>()
.As<ICostCalculator>();
builder
.RegisterType<ShopingCart>()
.As<IShopingCart>();
}
}
52
CommonLogicModule
public class CommonLogicModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder
.RegisterType<VolumeCostCalculator>()
.As<ICostCalculator>();
builder
.RegisterType<ShopingCart>()
.As<IShopingCart>();
}
}
53
Common.CompositionRoot
var builder = new ContainerBuilder();
builder.RegisterModule<CommonLogicModule>();
var container = builder.Build();
var costCalculator = container.Resolve<ICostCalculator>();
// VolumeCostCalculator
54
Common.CompositionRoot
var builder = new ContainerBuilder();
builder.RegisterModule<CommonLogicModule>();
var container = builder.Build();
var costCalculator = container.Resolve<ICostCalculator>();
// VolumeCostCalculator
55
Custom.Logic - WeightCostCalculator
internal class WeightCostCalculator : ICostCalculator
{
public int GetDeliveryCost(Product p)
{
return p.Weight;
}
}
56
Custom.Logic - WeightCostCalculator
internal class WeightCostCalculator : ICostCalculator
{
public int GetDeliveryCost(Product p)
{
return p.Weight;
}
}
57
Custom.Logic - WeightCostCalculator
internal class WeightCostCalculator : ICostCalculator
{
public int GetDeliveryCost(Product p)
{
return p.Weight;
}
}
58
CustomLogicModule
public class CustomLogicModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder
.RegisterType<WeightCostCalculator>()
.As<ICostCalculator>();
}
}
59
Custom.CompositionRoot
var builder = new ContainerBuilder();
// Порядок регистрации модулей важен
builder.RegisterModule<CommonLogicModule>();
builder.RegisterModule<CustomLogicModule>();
var container = builder.Build();
var costCalculator = container.Resolve<ICostCalculator>();
// WeightCostCalculator
60
Custom.CompositionRoot
var builder = new ContainerBuilder();
// Порядок регистрации модулей важен
builder.RegisterModule<CommonLogicModule>();
builder.RegisterModule<CustomLogicModule>();
var container = builder.Build();
var costCalculator = container.Resolve<ICostCalculator>();
// WeightCostCalculator
61
Для поддержки нескольких заказчиков
• Система должна соответствовать IoC
• Маленький и средний проект
• Модуль – слой (горизонтальная структура)
• Загрузка императивная
• Большой проект
• Модуль – плагин (вертикальная структура)
• Загрузка декларативная
• Выгода, если плагинов не менее 3
62
Что делать дальше
• Сделайте проект по IoC, пригодится 
• Посмотреть модули Prism, Autofac, MEF
• Нужны ли вам модули? Какие?
• Реализуйте модули!
63
Материалы по модулям
Пример к докладу на гитхабе
https://github.com/denis-tsv/Modules
Prism
https://msdn.microsoft.com/en-us/library/gg406140.aspx
http://habrahabr.ru/post/176863/
Autofac
http://docs.autofac.org/en/latest/configuration/modules.html
MEF
https://msdn.microsoft.com/ru-ru/library/dd460648(v=vs.110).aspx
Dependency Injection in .NET
http://www.ozon.ru/context/detail/id/35161137/
http://smarly.net/dependency-injection-in-net/
64
Спасибо за внимание
Денис Цветцих
den.tsvettsih@yandex.ru
65
Если система не соответствует IoC
public class Service
{
public static Service Instance { get; }
}
66
Решение 1: переделать на IoC
• Классы используют интерфейсы
• В модуле – класс, регистрирующий
сервисы в IoC контейнере
• В Composition Root – работа с модулями, а
не сервисами
67
Решение 2: ServiceLocator
• Добавить интерфейс для сервиса
• В CompositionRoot базовой реализации
кладем в ServiceLocator базовую
реализацию
• В кастомном CompositionRoot кладем в
ServiceLocator кастомную реализацию
68
Решение 2: ServiceLocator
Вместо статического свойства
Service.Instance
Используем реализацию из сервис-локатора
ServiceLocator.Current.GetInstance<IService>();
69
Спасибо за внимание
Денис Цветцих
den.tsvettsih@yandex.ru

Модульная структура. Цветцих Денис D2D Just.NET

  • 1.
    Модульная структура – бытьили не быть? Денис Цветцих АстроСофт http://www.astrosoft.ru/ D2D Just.NET 13 февраля 2016 dev2dev.ru
  • 2.
    2 Кто я? • 7лет .NET • Разработка корпоративных приложений • Паттерны, архитектура • Power Tools
  • 3.
    3 Проекты для несколькихзаказчиков • Биллинговая система • Внедрена 20-30 заказчикам с доработками • MES-система • Внедрена 150 заказчикам, для 10 есть доработки В обоих случаях доработки для заказчиков стали самостоятельными проектами
  • 4.
    4 Как все начиналось Как-тотак Мы уже продали систему другому заказчику! Но нужно чуть-чуть доработать. И быстро! Или так Мы напишем проект для заказчика А потом сделаем из него продукт! И заканчивается одинаково Нужно поддерживать несколько версий проекта Кому знакомо?
  • 5.
    5 Что делать –вроде бы понятно • Разделить проект на части (модули) • Для разных заказчиков • повторно использовать общие модули • реализовать специфичные модули
  • 6.
    6 Как это сделать– непонятно • Как устроен проект, позволяющий повторно использовать свои модули? • Как делить функционал на модули? • Как собрать проект из модулей?
  • 7.
  • 8.
    8 Проект для одногозаказчика UI Logic DataAccess Нельзя повторно использовать части, так как они зависят от нижних уровней
  • 9.
    9 Inversion of Control •Модули верхнего уровня не должны зависеть от модулей нижнего уровня. И те, и другие должны зависеть от абстракции. • Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.
  • 10.
    10 IoC проект UI Logic DataAccess interfaces Можноповторно использовать части системы Logic interfaces DataAccess
  • 11.
  • 12.
    12 Модуль Модуль – компонентпрограммы, имеющий • назначение • контракт (реализуемые и используемые сервисы) Что такое модуль в вашей системе – решаете сами 
  • 13.
    13 Какие бывают модули Пакет(Nuget) одна или несколько сборок может зависеть от других модулей Плагин одна или несколько сборок не зависит от других модулей
  • 14.
    14 Модульное приложение Состоит из: •Оболочка (Shell) • Инфраструктура • Загрузка модулей • Модули
  • 15.
    15 Горизонтальная декомпозиция (пакеты) Shell(Интернет-магазин) UI модуль UI личного кабинета Logic модуль UI корзины Оформление заказа Изменение настроек Data Access модуль Сохранение настроек Сохранение заказа
  • 16.
    16 Когда использовать • Одинпроект, много заказчиков • Не нужна новая реализация всех сервисов • Кастомные модули конфигурируют и переопределяют часть базового функционала
  • 17.
    17 Вертикальная декомпозиция (плагины) Shell(Visual Studio) Модуль (плагин) UI services Logic services DataAccess services Resharper (плагин) UI формы Рефакторинги Сохранение настроек
  • 18.
    18 Когда использовать • Система,состоящая из подсистем • Подсистема – это плагин • Линейка взаимодополняющих продуктов • В оболочке один плагин – один продукт • Если плагинов >= 3, тогда profit  • Проектировать плагины нужно сразу 
  • 19.
  • 20.
    20 Composition Root Composition root– единственная точка в приложении, где определяется какие реализации соответствуют каким интерфейсам Располагается в Shell рядом со входом: • Global.asax • App.xaml.cs • Инфраструктура (Application, Bootstrapper)
  • 21.
    21 Composition Root безмодулей protected override void OnStartup(StartupEventArgs e) { var builder = new ContainerBuilder(); // Data Access service builder.RegisterType<Repository>().As<IRepository>(); // Business Logic service builder.RegisterType<LogicService>().As<ILogicService>(); // User Interface service builder.RegisterType<NavigationService>().As<INavigationService>(); var container = builder.Build(); }
  • 22.
    22 Composition Root безмодулей protected override void OnStartup(StartupEventArgs e) { var builder = new ContainerBuilder(); // Data Access service builder.RegisterType<Repository>().As<IRepository>(); // Business Logic service builder.RegisterType<LogicService>().As<ILogicService>(); // User Interface service builder.RegisterType<NavigationService>().As<INavigationService>(); var container = builder.Build(); }
  • 23.
    23 Composition Root безмодулей protected override void OnStartup(StartupEventArgs e) { var builder = new ContainerBuilder(); // Data Access service builder.RegisterType<Repository>().As<IRepository>(); // Business Logic service builder.RegisterType<LogicService>().As<ILogicService>(); // User Interface service builder.RegisterType<NavigationService>().As<INavigationService>(); var container = builder.Build(); }
  • 24.
    24 Composition Root безмодулей protected override void OnStartup(StartupEventArgs e) { var builder = new ContainerBuilder(); // Data Access service builder.RegisterType<Repository>().As<IRepository>(); // Business Logic service builder.RegisterType<LogicService>().As<ILogicService>(); // User Interface service builder.RegisterType<NavigationService>().As<INavigationService>(); var container = builder.Build(); }
  • 25.
    25 Особенности Чем плохо • Непонятно,какие сервисы из какого модуля • Нельзя инкапсулировать реализацию сервисов • Повторное использовние модуля – копипаст регистрации сервисов в контейнере Чем хорошо • Модули не зависят от IoC контейнера
  • 26.
    26 Composition Root смодулями • В каждом модуле – специальный класс, регистрирующий сервисы этого модуля • В Composition Root – регистрация модулей, а не сервисов
  • 27.
    27 Варианты загрузки модулей •Императивный • Декларативный
  • 28.
    28 Императивный • Composition Rootотдает модулю IoC контейнер • Модуль регистрирует в контейнере свои сервисы
  • 29.
    29 Autofac: модуль public classAutofacModule : Module { protected override void Load(ContainerBuilder builder) { // Регистрируем сервисы builder.RegisterType<Service>().As<IService>(); } }
  • 30.
    30 Autofac: модуль public classAutofacModule : Module { protected override void Load(ContainerBuilder builder) { // Регистрируем сервисы builder.RegisterType<Service>().As<IService>(); } }
  • 31.
    31 Autofac: Composition Root privateIContainer Container; protected override void OnStartup(StartupEventArgs e) { var builder = new ContainerBuilder(); builder.RegisterModule<AutofacModule>(); Container = builder.Build(); }
  • 32.
    32 Autofac: Composition Root privateIContainer Container; protected override void OnStartup(StartupEventArgs e) { var builder = new ContainerBuilder(); builder.RegisterModule<AutofacModule>(); Container = builder.Build(); }
  • 33.
    33 Autofac: Composition Root privateIContainer Container; protected override void OnStartup(StartupEventArgs e) { var builder = new ContainerBuilder(); builder.RegisterModule<AutofacModule>(); Container = builder.Build(); }
  • 34.
    34 Особенности императивного варианта Достоинства •Показывает сервисы модуля • Инкапсуляция реализации сервисов • Просто перейти на другой IoC контейнер • Быстрый Недостатки • Не подходит для плагинов (небезопасный) • Порядок загрузки модулей может быть важен
  • 35.
    35 Декларативный • В модулеопределение сервисов при помощи метаданных • Атрибуты • Реализация интерфейсов • Composition Root анализирует метаданные при помощи Reflection
  • 36.
    36 MEF: модуль public interfaceIService { } [Export(typeof(IService))] public class Service : IService { }
  • 37.
    37 MEF: модуль public interfaceIService { } [Export(typeof(IService))] public class Service : IService { }
  • 38.
    38 MEF: Composition Root varconfiguration = new ContainerConfiguration() .WithAssembly(typeof (IService).Assembly); var container = configuration.CreateContainer();
  • 39.
    39 MEF: Composition Root varconfiguration = new ContainerConfiguration() .WithAssembly(typeof (IService).Assembly); var container = configuration.CreateContainer();
  • 40.
    40 Особенности декларативного варианта Достоинства •Позволяет писать плагины • Порядок загрузки модулей произвольный Недостатки • Нельзя инкапсулировать реализации сервисов • Нельзя явно увидеть список сервисов модуля • Сложнее перейти на другой IoC контейнер • Загрузка может быть долгой
  • 41.
    41 Пример: интернет -магазин Товары имеют вес и размеры (длина, ширина, высота) Корзина считает стоимость доставки товаров Базовый сценарий: расчет исходя из объема Кастомный сценарий: расчет исходя из веса Система реализована по принципу IoC  Нужно реализовать кастомный сценарий
  • 42.
    42 Common.Domain public class Product { publicint Id { get; set; } public int Height { get; set; } public int Width { get; set; } public int Length { get; set; } public int Weight { get; set; } }
  • 43.
    43 Common.Domain public class Product { publicint Id { get; set; } public int Height { get; set; } public int Width { get; set; } public int Length { get; set; } public int Weight { get; set; } }
  • 44.
    44 Common.Logic - CostCalculator publicinterface ICostCalculator { int GetDeliveryCost(Product product); } internal class VolumeCostCalculator : ICostCalculator { public int GetDeliveryCost(Product p) { return p.Height * p.Width * p.Length; } }
  • 45.
    45 Common.Logic - CostCalculator publicinterface ICostCalculator { int GetDeliveryCost(Product product); } internal class VolumeCostCalculator : ICostCalculator { public int GetDeliveryCost(Product p) { return p.Height * p.Width * p.Length; } }
  • 46.
    46 Common.Logic - CostCalculator publicinterface ICostCalculator { int GetDeliveryCost(Product product); } internal class VolumeCostCalculator : ICostCalculator { public int GetDeliveryCost(Product p) { return p.Height * p.Width * p.Length; } }
  • 47.
    47 Common.Logic - CostCalculator publicinterface ICostCalculator { int GetDeliveryCost(Product product); } internal class VolumeCostCalculator : ICostCalculator { public int GetDeliveryCost(Product p) { return p.Height * p.Width * p.Length; } }
  • 48.
    48 Common.Logic - ShopingCart internalclass ShopingCart : IShopingCart { private ICostCalculator _calculator; private List<Product> _products = new List<Product>(); public ShopingCart(ICostCalculator calculator) { _calculator = costCalculator; } public int GetDeliveryCost() { return _products .Sum(p => _calculator.GetDeliveryCost(p)); } }
  • 49.
    49 Common.Logic - ShopingCart internalclass ShopingCart : IShopingCart { private ICostCalculator _calculator; private List<Product> _products = new List<Product>(); public ShopingCart(ICostCalculator calculator) { _calculator = costCalculator; } public int GetDeliveryCost() { return _products .Sum(p => _calculator.GetDeliveryCost(p)); } }
  • 50.
    50 Common.Logic - ShopingCart internalclass ShopingCart : IShopingCart { private ICostCalculator _calculator; private List<Product> _products = new List<Product>(); public ShopingCart(ICostCalculator calculator) { _calculator = costCalculator; } public int GetDeliveryCost() { return _products .Sum(p => _calculator.GetDeliveryCost(p)); } }
  • 51.
    51 CommonLogicModule public class CommonLogicModule: Module { protected override void Load(ContainerBuilder builder) { builder .RegisterType<VolumeCostCalculator>() .As<ICostCalculator>(); builder .RegisterType<ShopingCart>() .As<IShopingCart>(); } }
  • 52.
    52 CommonLogicModule public class CommonLogicModule: Module { protected override void Load(ContainerBuilder builder) { builder .RegisterType<VolumeCostCalculator>() .As<ICostCalculator>(); builder .RegisterType<ShopingCart>() .As<IShopingCart>(); } }
  • 53.
    53 Common.CompositionRoot var builder =new ContainerBuilder(); builder.RegisterModule<CommonLogicModule>(); var container = builder.Build(); var costCalculator = container.Resolve<ICostCalculator>(); // VolumeCostCalculator
  • 54.
    54 Common.CompositionRoot var builder =new ContainerBuilder(); builder.RegisterModule<CommonLogicModule>(); var container = builder.Build(); var costCalculator = container.Resolve<ICostCalculator>(); // VolumeCostCalculator
  • 55.
    55 Custom.Logic - WeightCostCalculator internalclass WeightCostCalculator : ICostCalculator { public int GetDeliveryCost(Product p) { return p.Weight; } }
  • 56.
    56 Custom.Logic - WeightCostCalculator internalclass WeightCostCalculator : ICostCalculator { public int GetDeliveryCost(Product p) { return p.Weight; } }
  • 57.
    57 Custom.Logic - WeightCostCalculator internalclass WeightCostCalculator : ICostCalculator { public int GetDeliveryCost(Product p) { return p.Weight; } }
  • 58.
    58 CustomLogicModule public class CustomLogicModule: Module { protected override void Load(ContainerBuilder builder) { builder .RegisterType<WeightCostCalculator>() .As<ICostCalculator>(); } }
  • 59.
    59 Custom.CompositionRoot var builder =new ContainerBuilder(); // Порядок регистрации модулей важен builder.RegisterModule<CommonLogicModule>(); builder.RegisterModule<CustomLogicModule>(); var container = builder.Build(); var costCalculator = container.Resolve<ICostCalculator>(); // WeightCostCalculator
  • 60.
    60 Custom.CompositionRoot var builder =new ContainerBuilder(); // Порядок регистрации модулей важен builder.RegisterModule<CommonLogicModule>(); builder.RegisterModule<CustomLogicModule>(); var container = builder.Build(); var costCalculator = container.Resolve<ICostCalculator>(); // WeightCostCalculator
  • 61.
    61 Для поддержки несколькихзаказчиков • Система должна соответствовать IoC • Маленький и средний проект • Модуль – слой (горизонтальная структура) • Загрузка императивная • Большой проект • Модуль – плагин (вертикальная структура) • Загрузка декларативная • Выгода, если плагинов не менее 3
  • 62.
    62 Что делать дальше •Сделайте проект по IoC, пригодится  • Посмотреть модули Prism, Autofac, MEF • Нужны ли вам модули? Какие? • Реализуйте модули!
  • 63.
    63 Материалы по модулям Примерк докладу на гитхабе https://github.com/denis-tsv/Modules Prism https://msdn.microsoft.com/en-us/library/gg406140.aspx http://habrahabr.ru/post/176863/ Autofac http://docs.autofac.org/en/latest/configuration/modules.html MEF https://msdn.microsoft.com/ru-ru/library/dd460648(v=vs.110).aspx Dependency Injection in .NET http://www.ozon.ru/context/detail/id/35161137/ http://smarly.net/dependency-injection-in-net/
  • 64.
    64 Спасибо за внимание ДенисЦветцих den.tsvettsih@yandex.ru
  • 65.
    65 Если система несоответствует IoC public class Service { public static Service Instance { get; } }
  • 66.
    66 Решение 1: переделатьна IoC • Классы используют интерфейсы • В модуле – класс, регистрирующий сервисы в IoC контейнере • В Composition Root – работа с модулями, а не сервисами
  • 67.
    67 Решение 2: ServiceLocator •Добавить интерфейс для сервиса • В CompositionRoot базовой реализации кладем в ServiceLocator базовую реализацию • В кастомном CompositionRoot кладем в ServiceLocator кастомную реализацию
  • 68.
    68 Решение 2: ServiceLocator Вместостатического свойства Service.Instance Используем реализацию из сервис-локатора ServiceLocator.Current.GetInstance<IService>();
  • 69.
    69 Спасибо за внимание ДенисЦветцих den.tsvettsih@yandex.ru

Editor's Notes

  • #19 Не обязательно работать в MS и писать студию или работать в JetBrains и писать Resharper, тоже расширяемый плагинами. 3 – свой опыт, Гласс факты и заблуждения, Басс архитектура ПО на практике