69
Модульная структура – быть или не быть? Денис Цветцих АстроСофт http://www.astrosoft.ru/ D2D Just.NET 13 февраля 2016 dev2dev.ru

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

Embed Size (px)

Citation preview

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

Модульная структура – быть или не быть?

Денис ЦветцихАстроСофт

http://www.astrosoft.ru/

D2D Just.NET13 февраля 2016dev2dev.ru

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

2

Кто я?• 7 лет .NET• Разработка корпоративных

приложений• Паттерны, архитектура• Power Tools

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

3

Проекты для нескольких заказчиков

• Биллинговая система• Внедрена 20-30 заказчикам с

доработками• MES-система• Внедрена 150 заказчикам, для 10 есть

доработки

В обоих случаях доработки для заказчиков стали самостоятельными проектами

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

4

Как все начиналосьКак-то так

Мы уже продали систему другому заказчику!Но нужно чуть-чуть доработать. И быстро!

Или такМы напишем проект для заказчикаА потом сделаем из него продукт!

И заканчивается одинаковоНужно поддерживать несколько версий проекта

Кому знакомо?

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

5

Что делать – вроде бы понятно

• Разделить проект на части (модули)

• Для разных заказчиков • повторно использовать общие

модули• реализовать специфичные модули

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

6

Как это сделать – непонятно

• Как устроен проект, позволяющий повторно использовать свои модули?

• Как делить функционал на модули?

• Как собрать проект из модулей?

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

КАК КОНСТРУИРОВАТЬ ПРОЕКТ

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

8

Проект для одного заказчика

UI

Logic

DataAccess

Нельзя повторно использовать части, так как они зависят от нижних уровней

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

9

Inversion of Control• Модули верхнего уровня не

должны зависеть от модулей нижнего уровня. И те, и другие должны зависеть от абстракции.

• Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.

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

10

IoC проектUI

Logic

DataAccess interfaces

Можно повторно использовать части системы

Logic interfaces

DataAccess

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

ДЕКОМПОЗИЦИЯ

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

12

МодульМодуль – компонент программы,

имеющий•назначение•контракт (реализуемые и

используемые сервисы)

Что такое модуль в вашей системе – решаете сами

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

13

Какие бывают модулиПакет (Nuget)

одна или несколько сборокможет зависеть от других модулей

Плагинодна или несколько сборокне зависит от других модулей

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

14

Модульное приложениеСостоит из:• Оболочка (Shell)• Инфраструктура• Загрузка модулей

• Модули

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

15

Горизонтальная декомпозиция (пакеты)Shell (Интернет-магазин)

UI модульUI личного кабинета

Logic модуль

UI корзины

Оформление заказа

Изменение настроек

Data Access модуль

Сохранение настроек

Сохранение заказа

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

16

Когда использовать• Один проект, много заказчиков • Не нужна новая реализация всех

сервисов• Кастомные модули конфигурируют и

переопределяют часть базового функционала

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

17

Вертикальная декомпозиция (плагины)

Shell (Visual Studio)

Модуль (плагин)

UI services

Logic services

DataAccess services

Resharper (плагин)

UI формы

Рефакторинги

Сохранение настроек

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

18

Когда использовать• Система, состоящая из подсистем• Подсистема – это плагин

• Линейка взаимодополняющих продуктов• В оболочке один плагин – один

продукт

• Если плагинов >= 3, тогда profit • Проектировать плагины нужно

сразу

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

ЗАГРУЗКА

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

20

Composition RootComposition root – единственная

точка в приложении, где определяется какие реализации соответствуют каким интерфейсам

Располагается в Shell рядом со входом:• Global.asax• App.xaml.cs• Инфраструктура (Application,

Bootstrapper)

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

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();}

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

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();}

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

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();}

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

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();}

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

25

ОсобенностиЧем плохо• Непонятно, какие сервисы из какого

модуля• Нельзя инкапсулировать реализацию

сервисов• Повторное использовние модуля –

копипаст регистрации сервисов в контейнере

Чем хорошо• Модули не зависят от IoC контейнера

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

26

Composition Root с модулями

• В каждом модуле – специальный класс, регистрирующий сервисы этого модуля

• В Composition Root – регистрация модулей, а не сервисов

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

27

Варианты загрузки модулей

• Императивный• Декларативный

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

28

Императивный• Composition Root отдает модулю IoC

контейнер• Модуль регистрирует в контейнере

свои сервисы

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

29

Autofac: модульpublic class AutofacModule : Module{ protected override void Load(ContainerBuilder builder) { // Регистрируем сервисы builder.RegisterType<Service>().As<IService>(); }}

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

30

Autofac: модульpublic class AutofacModule : Module{ protected override void Load(ContainerBuilder builder) { // Регистрируем сервисы builder.RegisterType<Service>().As<IService>(); }}

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

31

Autofac: Composition Root

private IContainer Container;

protected override void OnStartup(StartupEventArgs e){ var builder = new ContainerBuilder();

builder.RegisterModule<AutofacModule>();

Container = builder.Build();

}

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

32

Autofac: Composition Root

private IContainer Container;

protected override void OnStartup(StartupEventArgs e){ var builder = new ContainerBuilder();

builder.RegisterModule<AutofacModule>();

Container = builder.Build();

}

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

33

Autofac: Composition Root

private IContainer Container;

protected override void OnStartup(StartupEventArgs e){ var builder = new ContainerBuilder();

builder.RegisterModule<AutofacModule>();

Container = builder.Build();

}

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

34

Особенности императивного варианта

Достоинства• Показывает сервисы модуля• Инкапсуляция реализации сервисов• Просто перейти на другой IoC контейнер• Быстрый

Недостатки• Не подходит для плагинов (небезопасный)• Порядок загрузки модулей может быть

важен

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

35

Декларативный• В модуле определение сервисов

при помощи метаданных• Атрибуты• Реализация интерфейсов

• Composition Root анализирует метаданные при помощи Reflection

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

36

MEF: модульpublic interface IService{}

[Export(typeof(IService))]public class Service : IService{

}

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

37

MEF: модульpublic interface IService{}

[Export(typeof(IService))]public class Service : IService{

}

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

38

MEF: Composition Rootvar configuration = new ContainerConfiguration() .WithAssembly(typeof (IService).Assembly);

var container = configuration.CreateContainer();

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

39

MEF: Composition Rootvar configuration = new ContainerConfiguration() .WithAssembly(typeof (IService).Assembly);

var container = configuration.CreateContainer();

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

40

Особенности декларативного варианта

Достоинства• Позволяет писать плагины• Порядок загрузки модулей произвольный

Недостатки• Нельзя инкапсулировать реализации

сервисов • Нельзя явно увидеть список сервисов модуля• Сложнее перейти на другой IoC контейнер • Загрузка может быть долгой

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

41

Пример: интернет - магазин

Товары имеют вес и размеры (длина, ширина, высота)

Корзина считает стоимость доставки товаров

Базовый сценарий: расчет исходя из объемаКастомный сценарий: расчет исходя из веса

Система реализована по принципу IoC Нужно реализовать кастомный сценарий

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

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; }}

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

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; }}

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

44

Common.Logic - CostCalculatorpublic interface ICostCalculator{ int GetDeliveryCost(Product product);}

internal class VolumeCostCalculator : ICostCalculator{ public int GetDeliveryCost(Product p) { return p.Height * p.Width * p.Length; }}

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

45

Common.Logic - CostCalculatorpublic interface ICostCalculator{ int GetDeliveryCost(Product product);}

internal class VolumeCostCalculator : ICostCalculator{ public int GetDeliveryCost(Product p) { return p.Height * p.Width * p.Length; }}

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

46

Common.Logic - CostCalculatorpublic interface ICostCalculator{ int GetDeliveryCost(Product product);}

internal class VolumeCostCalculator : ICostCalculator{ public int GetDeliveryCost(Product p) { return p.Height * p.Width * p.Length; }}

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

47

Common.Logic - CostCalculatorpublic interface ICostCalculator{ int GetDeliveryCost(Product product);}

internal class VolumeCostCalculator : ICostCalculator{ public int GetDeliveryCost(Product p) { return p.Height * p.Width * p.Length; }}

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

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)); }}

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

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)); }}

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

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)); }}

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

51

CommonLogicModulepublic class CommonLogicModule : Module{ protected override void Load(ContainerBuilder

builder) { builder .RegisterType<VolumeCostCalculator>() .As<ICostCalculator>();

builder .RegisterType<ShopingCart>() .As<IShopingCart>(); }}

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

52

CommonLogicModulepublic class CommonLogicModule : Module{ protected override void Load(ContainerBuilder

builder) { builder .RegisterType<VolumeCostCalculator>() .As<ICostCalculator>();

builder .RegisterType<ShopingCart>() .As<IShopingCart>(); }}

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

53

Common.CompositionRoot

var builder = new ContainerBuilder();

builder.RegisterModule<CommonLogicModule>();

var container = builder.Build();

var costCalculator = container.Resolve<ICostCalculator>();

// VolumeCostCalculator

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

54

Common.CompositionRoot

var builder = new ContainerBuilder();

builder.RegisterModule<CommonLogicModule>();

var container = builder.Build();

var costCalculator = container.Resolve<ICostCalculator>();

// VolumeCostCalculator

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

55

Custom.Logic - WeightCostCalculator

internal class WeightCostCalculator : ICostCalculator

{ public int GetDeliveryCost(Product p) { return p.Weight; }}

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

56

Custom.Logic - WeightCostCalculator

internal class WeightCostCalculator : ICostCalculator

{ public int GetDeliveryCost(Product p) { return p.Weight; }}

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

57

Custom.Logic - WeightCostCalculator

internal class WeightCostCalculator : ICostCalculator

{ public int GetDeliveryCost(Product p) { return p.Weight; }}

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

58

CustomLogicModulepublic class CustomLogicModule : Module{ protected override void Load(ContainerBuilder

builder) { builder .RegisterType<WeightCostCalculator>() .As<ICostCalculator>(); }}

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

59

Custom.CompositionRootvar builder = new ContainerBuilder();

// Порядок регистрации модулей важенbuilder.RegisterModule<CommonLogicModule>();builder.RegisterModule<CustomLogicModule>();

var container = builder.Build();

var costCalculator = container.Resolve<ICostCalculator>();

// WeightCostCalculator

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

60

Custom.CompositionRootvar builder = new ContainerBuilder();

// Порядок регистрации модулей важенbuilder.RegisterModule<CommonLogicModule>();builder.RegisterModule<CustomLogicModule>();

var container = builder.Build();

var costCalculator = container.Resolve<ICostCalculator>();

// WeightCostCalculator

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

61

Для поддержки нескольких заказчиков

• Система должна соответствовать IoC

• Маленький и средний проект• Модуль – слой (горизонтальная

структура)• Загрузка императивная

• Большой проект• Модуль – плагин (вертикальная

структура)• Загрузка декларативная• Выгода, если плагинов не менее 3

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

62

Что делать дальше• Сделайте проект по IoC,

пригодится • Посмотреть модули Prism, Autofac,

MEF• Нужны ли вам модули? Какие?• Реализуйте модули!

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

64

Спасибо за вниманиеДенис Цветцих

[email protected]

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

65

Если система не соответствует IoC

public class Service{ public static Service Instance { get; }}

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

66

Решение 1: переделать на IoC

• Классы используют интерфейсы• В модуле – класс, регистрирующий

сервисы в IoC контейнере• В Composition Root – работа с

модулями, а не сервисами

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

67

Решение 2: ServiceLocator

• Добавить интерфейс для сервиса• В CompositionRoot базовой

реализации кладем в ServiceLocator базовую реализацию

• В кастомном CompositionRoot кладем в ServiceLocator кастомную реализацию

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

68

Решение 2: ServiceLocator

Вместо статического свойстваService.Instance

Используем реализацию из сервис-локатора

ServiceLocator.Current.GetInstance<IService>();

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

69

Спасибо за вниманиеДенис Цветцих

[email protected]