SlideShare a Scribd company logo
1 of 69
Модульная структура –
быть или не быть?
Денис Цветцих
АстроСофт
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

More Related Content

What's hot

C++ STL & Qt. Занятие 07.
C++ STL & Qt. Занятие 07.C++ STL & Qt. Занятие 07.
C++ STL & Qt. Занятие 07.Igor Shkulipa
 
Архитектура. Доступноять программных систем.
Архитектура. Доступноять программных систем.Архитектура. Доступноять программных систем.
Архитектура. Доступноять программных систем.Dima Dzuba
 
C++ STL & Qt. Занятие 04.
C++ STL & Qt. Занятие 04.C++ STL & Qt. Занятие 04.
C++ STL & Qt. Занятие 04.Igor Shkulipa
 
C++ STL & Qt. Занятие 01.
C++ STL & Qt. Занятие 01.C++ STL & Qt. Занятие 01.
C++ STL & Qt. Занятие 01.Igor Shkulipa
 
C++ STL & Qt. Занятие 02.
C++ STL & Qt. Занятие 02.C++ STL & Qt. Занятие 02.
C++ STL & Qt. Занятие 02.Igor Shkulipa
 
C++ STL & Qt. Занятие 08.
C++ STL & Qt. Занятие 08.C++ STL & Qt. Занятие 08.
C++ STL & Qt. Занятие 08.Igor Shkulipa
 
Java осень 2014 занятие 1
Java осень 2014 занятие 1Java осень 2014 занятие 1
Java осень 2014 занятие 1Technopark
 
C++ STL & Qt. Занятие 10.
C++ STL & Qt. Занятие 10.C++ STL & Qt. Занятие 10.
C++ STL & Qt. Занятие 10.Igor Shkulipa
 
Клиент-серверное взаимодействие под android в деталях
Клиент-серверное взаимодействие под android в деталяхКлиент-серверное взаимодействие под android в деталях
Клиент-серверное взаимодействие под android в деталяхKirill Zotin
 
C++ STL & Qt. Занятие 11.
C++ STL & Qt. Занятие 11.C++ STL & Qt. Занятие 11.
C++ STL & Qt. Занятие 11.Igor Shkulipa
 
C++ STL & Qt. Занятие 03.
C++ STL & Qt. Занятие 03.C++ STL & Qt. Занятие 03.
C++ STL & Qt. Занятие 03.Igor Shkulipa
 
Асинхронность и сопрограммы
Асинхронность и сопрограммыАсинхронность и сопрограммы
Асинхронность и сопрограммыPlatonov Sergey
 
Лекция Android. БД SQLite, ContentProvider, Loader
Лекция Android. БД SQLite, ContentProvider, LoaderЛекция Android. БД SQLite, ContentProvider, Loader
Лекция Android. БД SQLite, ContentProvider, LoaderАлександр Брич
 
Модульная структура
Модульная структураМодульная структура
Модульная структураDenis Tsvettsih
 
Android - 13 - Database
Android - 13 - DatabaseAndroid - 13 - Database
Android - 13 - DatabaseNoveo
 
Григорий Демченко — Асинхронное программирование и сопрограммы
Григорий Демченко — Асинхронное программирование и сопрограммыГригорий Демченко — Асинхронное программирование и сопрограммы
Григорий Демченко — Асинхронное программирование и сопрограммыYandex
 
Testing RIA with Selenium
Testing RIA with SeleniumTesting RIA with Selenium
Testing RIA with SeleniumSergey Shvets
 
Практика использования Dependency Injection
Практика использования Dependency InjectionПрактика использования Dependency Injection
Практика использования Dependency InjectionPlatonov Sergey
 

What's hot (20)

C++ STL & Qt. Занятие 07.
C++ STL & Qt. Занятие 07.C++ STL & Qt. Занятие 07.
C++ STL & Qt. Занятие 07.
 
Архитектура. Доступноять программных систем.
Архитектура. Доступноять программных систем.Архитектура. Доступноять программных систем.
Архитектура. Доступноять программных систем.
 
C++ STL & Qt. Занятие 04.
C++ STL & Qt. Занятие 04.C++ STL & Qt. Занятие 04.
C++ STL & Qt. Занятие 04.
 
C++ STL & Qt. Занятие 01.
C++ STL & Qt. Занятие 01.C++ STL & Qt. Занятие 01.
C++ STL & Qt. Занятие 01.
 
C++ STL & Qt. Занятие 02.
C++ STL & Qt. Занятие 02.C++ STL & Qt. Занятие 02.
C++ STL & Qt. Занятие 02.
 
C++ STL & Qt. Занятие 08.
C++ STL & Qt. Занятие 08.C++ STL & Qt. Занятие 08.
C++ STL & Qt. Занятие 08.
 
Java осень 2014 занятие 1
Java осень 2014 занятие 1Java осень 2014 занятие 1
Java осень 2014 занятие 1
 
Coding like a sex
Coding like a sexCoding like a sex
Coding like a sex
 
C++ STL & Qt. Занятие 10.
C++ STL & Qt. Занятие 10.C++ STL & Qt. Занятие 10.
C++ STL & Qt. Занятие 10.
 
Клиент-серверное взаимодействие под android в деталях
Клиент-серверное взаимодействие под android в деталяхКлиент-серверное взаимодействие под android в деталях
Клиент-серверное взаимодействие под android в деталях
 
C++ STL & Qt. Занятие 11.
C++ STL & Qt. Занятие 11.C++ STL & Qt. Занятие 11.
C++ STL & Qt. Занятие 11.
 
C++ STL & Qt. Занятие 03.
C++ STL & Qt. Занятие 03.C++ STL & Qt. Занятие 03.
C++ STL & Qt. Занятие 03.
 
Асинхронность и сопрограммы
Асинхронность и сопрограммыАсинхронность и сопрограммы
Асинхронность и сопрограммы
 
Лекция Android. БД SQLite, ContentProvider, Loader
Лекция Android. БД SQLite, ContentProvider, LoaderЛекция Android. БД SQLite, ContentProvider, Loader
Лекция Android. БД SQLite, ContentProvider, Loader
 
Модульная структура
Модульная структураМодульная структура
Модульная структура
 
Bytecode
BytecodeBytecode
Bytecode
 
Android - 13 - Database
Android - 13 - DatabaseAndroid - 13 - Database
Android - 13 - Database
 
Григорий Демченко — Асинхронное программирование и сопрограммы
Григорий Демченко — Асинхронное программирование и сопрограммыГригорий Демченко — Асинхронное программирование и сопрограммы
Григорий Демченко — Асинхронное программирование и сопрограммы
 
Testing RIA with Selenium
Testing RIA with SeleniumTesting RIA with Selenium
Testing RIA with Selenium
 
Практика использования Dependency Injection
Практика использования Dependency InjectionПрактика использования Dependency Injection
Практика использования Dependency Injection
 

Viewers also liked

Всё будет в Ажуре. Завершинский Денис D2D Just.NET
Всё будет в Ажуре. Завершинский Денис D2D Just.NETВсё будет в Ажуре. Завершинский Денис D2D Just.NET
Всё будет в Ажуре. Завершинский Денис D2D Just.NETDev2Dev
 
Продукт или проект - Александр Борисов Dev2Dev v1.5 23.11.2014
Продукт или проект - Александр Борисов  Dev2Dev v1.5 23.11.2014Продукт или проект - Александр Борисов  Dev2Dev v1.5 23.11.2014
Продукт или проект - Александр Борисов Dev2Dev v1.5 23.11.2014Dev2Dev
 
Принцип YAGNI в управлении проектами - Анна Тарасенко Dev2Dev v2.0 30.05.2015
Принцип YAGNI в управлении проектами - Анна Тарасенко Dev2Dev v2.0 30.05.2015Принцип YAGNI в управлении проектами - Анна Тарасенко Dev2Dev v2.0 30.05.2015
Принцип YAGNI в управлении проектами - Анна Тарасенко Dev2Dev v2.0 30.05.2015Dev2Dev
 
Polytec PE valve
Polytec PE valvePolytec PE valve
Polytec PE valvepolytec
 
Apartes de la Charla: Una Especial Orden Ejecutiva de Barack Obama
Apartes de la Charla: Una Especial Orden Ejecutiva de Barack ObamaApartes de la Charla: Una Especial Orden Ejecutiva de Barack Obama
Apartes de la Charla: Una Especial Orden Ejecutiva de Barack ObamaSOCIEDAD JULIO GARAVITO
 
Avk mufta
Avk muftaAvk mufta
Avk muftapolytec
 
Avk 0630
Avk 0630Avk 0630
Avk 0630polytec
 
Применение в Enterprise-приложении графовой базы данных Neo4j - Антон Максимо...
Применение в Enterprise-приложении графовой базы данных Neo4j - Антон Максимо...Применение в Enterprise-приложении графовой базы данных Neo4j - Антон Максимо...
Применение в Enterprise-приложении графовой базы данных Neo4j - Антон Максимо...Dev2Dev
 
Трудности повторного использования
Трудности повторного использованияТрудности повторного использования
Трудности повторного использованияDev2Dev
 
Теории и практики функционального программирования.
Теории и практики функционального программирования.Теории и практики функционального программирования.
Теории и практики функционального программирования.Dev2Dev
 
Nemerle. метапрограммирование в .NET - Зимин Aлександр D2D Just.NET
Nemerle. метапрограммирование в .NET - Зимин Aлександр D2D Just.NETNemerle. метапрограммирование в .NET - Зимин Aлександр D2D Just.NET
Nemerle. метапрограммирование в .NET - Зимин Aлександр D2D Just.NETDev2Dev
 
RxJava + Retrofit
RxJava + RetrofitRxJava + Retrofit
RxJava + RetrofitDev2Dev
 
Micro orm для жизни. Кожевников Дмитрий D2D Just.NET
Micro orm для жизни. Кожевников Дмитрий D2D Just.NETMicro orm для жизни. Кожевников Дмитрий D2D Just.NET
Micro orm для жизни. Кожевников Дмитрий D2D Just.NETDev2Dev
 
Pankreas, mezenterija, zarnu traumatisks bojajums A. Gololobovs
Pankreas, mezenterija, zarnu traumatisks bojajums A. GololobovsPankreas, mezenterija, zarnu traumatisks bojajums A. Gololobovs
Pankreas, mezenterija, zarnu traumatisks bojajums A. GololobovsInga Nalivaiko
 
Amen's house.pt.1.mini.series.html.doc
Amen's house.pt.1.mini.series.html.docAmen's house.pt.1.mini.series.html.doc
Amen's house.pt.1.mini.series.html.dockhristianj
 

Viewers also liked (20)

Всё будет в Ажуре. Завершинский Денис D2D Just.NET
Всё будет в Ажуре. Завершинский Денис D2D Just.NETВсё будет в Ажуре. Завершинский Денис D2D Just.NET
Всё будет в Ажуре. Завершинский Денис D2D Just.NET
 
Продукт или проект - Александр Борисов Dev2Dev v1.5 23.11.2014
Продукт или проект - Александр Борисов  Dev2Dev v1.5 23.11.2014Продукт или проект - Александр Борисов  Dev2Dev v1.5 23.11.2014
Продукт или проект - Александр Борисов Dev2Dev v1.5 23.11.2014
 
Принцип YAGNI в управлении проектами - Анна Тарасенко Dev2Dev v2.0 30.05.2015
Принцип YAGNI в управлении проектами - Анна Тарасенко Dev2Dev v2.0 30.05.2015Принцип YAGNI в управлении проектами - Анна Тарасенко Dev2Dev v2.0 30.05.2015
Принцип YAGNI в управлении проектами - Анна Тарасенко Dev2Dev v2.0 30.05.2015
 
Polytec PE valve
Polytec PE valvePolytec PE valve
Polytec PE valve
 
Apartes de la Charla: Una Especial Orden Ejecutiva de Barack Obama
Apartes de la Charla: Una Especial Orden Ejecutiva de Barack ObamaApartes de la Charla: Una Especial Orden Ejecutiva de Barack Obama
Apartes de la Charla: Una Especial Orden Ejecutiva de Barack Obama
 
Avk gaz
Avk gazAvk gaz
Avk gaz
 
Avk mufta
Avk muftaAvk mufta
Avk mufta
 
Avk 0630
Avk 0630Avk 0630
Avk 0630
 
Применение в Enterprise-приложении графовой базы данных Neo4j - Антон Максимо...
Применение в Enterprise-приложении графовой базы данных Neo4j - Антон Максимо...Применение в Enterprise-приложении графовой базы данных Neo4j - Антон Максимо...
Применение в Enterprise-приложении графовой базы данных Neo4j - Антон Максимо...
 
Трудности повторного использования
Трудности повторного использованияТрудности повторного использования
Трудности повторного использования
 
Теории и практики функционального программирования.
Теории и практики функционального программирования.Теории и практики функционального программирования.
Теории и практики функционального программирования.
 
Nemerle. метапрограммирование в .NET - Зимин Aлександр D2D Just.NET
Nemerle. метапрограммирование в .NET - Зимин Aлександр D2D Just.NETNemerle. метапрограммирование в .NET - Зимин Aлександр D2D Just.NET
Nemerle. метапрограммирование в .NET - Зимин Aлександр D2D Just.NET
 
Proyecto de redes
Proyecto de redesProyecto de redes
Proyecto de redes
 
Nieru šūnu karcinoma
Nieru šūnu karcinomaNieru šūnu karcinoma
Nieru šūnu karcinoma
 
RxJava + Retrofit
RxJava + RetrofitRxJava + Retrofit
RxJava + Retrofit
 
Micro orm для жизни. Кожевников Дмитрий D2D Just.NET
Micro orm для жизни. Кожевников Дмитрий D2D Just.NETMicro orm для жизни. Кожевников Дмитрий D2D Just.NET
Micro orm для жизни. Кожевников Дмитрий D2D Just.NET
 
Feohromocitoma
FeohromocitomaFeohromocitoma
Feohromocitoma
 
Pankreas, mezenterija, zarnu traumatisks bojajums A. Gololobovs
Pankreas, mezenterija, zarnu traumatisks bojajums A. GololobovsPankreas, mezenterija, zarnu traumatisks bojajums A. Gololobovs
Pankreas, mezenterija, zarnu traumatisks bojajums A. Gololobovs
 
Amen's house.pt.1.mini.series.html.doc
Amen's house.pt.1.mini.series.html.docAmen's house.pt.1.mini.series.html.doc
Amen's house.pt.1.mini.series.html.doc
 
Refralbook.com
Refralbook.comRefralbook.com
Refralbook.com
 

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

Построение собственного JS SDK — зачем и как?
Построение собственного JS SDK — зачем и как?Построение собственного JS SDK — зачем и как?
Построение собственного JS SDK — зачем и как?buranLcme
 
"Webpack: 7 бед — один ответ" — Денис Измайлов, MoscowJS 17
"Webpack: 7 бед — один ответ" — Денис Измайлов, MoscowJS 17"Webpack: 7 бед — один ответ" — Денис Измайлов, MoscowJS 17
"Webpack: 7 бед — один ответ" — Денис Измайлов, MoscowJS 17MoscowJS
 
Юрий Василевский "Автоматизация в XCode"
Юрий Василевский "Автоматизация в XCode"Юрий Василевский "Автоматизация в XCode"
Юрий Василевский "Автоматизация в XCode"Yandex
 
Юрий Василевский «Автоматизация в XCode»
Юрий Василевский «Автоматизация в XCode»Юрий Василевский «Автоматизация в XCode»
Юрий Василевский «Автоматизация в XCode»Yandex
 
IT-инфраструктура. FAQ для разработчика
IT-инфраструктура. FAQ для разработчикаIT-инфраструктура. FAQ для разработчика
IT-инфраструктура. FAQ для разработчикаMikhail Chinkov
 
И снова разработка под iOS. Павел Тайкало
И снова разработка под iOS. Павел ТайкалоИ снова разработка под iOS. Павел Тайкало
И снова разработка под iOS. Павел ТайкалоStanfy
 
CQRS на практике. В поиске точки масштабирования и новых метафор
CQRS на практике. В поиске точки масштабирования и новых метафорCQRS на практике. В поиске точки масштабирования и новых метафор
CQRS на практике. В поиске точки масштабирования и новых метафорAlexander Byndyu
 
Создаем Drupal дистрибутив: от идеи до сопровождения
Создаем Drupal дистрибутив: от идеи до сопровожденияСоздаем Drupal дистрибутив: от идеи до сопровождения
Создаем Drupal дистрибутив: от идеи до сопровожденияOvadiah Myrgorod
 
Непрерывная интеграция при разработке баз данных. (Show version)
Непрерывная интеграция при разработке баз данных. (Show version)Непрерывная интеграция при разработке баз данных. (Show version)
Непрерывная интеграция при разработке баз данных. (Show version)Vladimir Bakhov
 
#3 "Webpack и Vue.JS: Создание больших приложений и их расширение" Кирилл Кай...
#3 "Webpack и Vue.JS: Создание больших приложений и их расширение" Кирилл Кай...#3 "Webpack и Vue.JS: Создание больших приложений и их расширение" Кирилл Кай...
#3 "Webpack и Vue.JS: Создание больших приложений и их расширение" Кирилл Кай...JSib
 
Микросервисная архитектура на базе CoreOS и Kubernetes
Микросервисная архитектура на базе CoreOS и KubernetesМикросервисная архитектура на базе CoreOS и Kubernetes
Микросервисная архитектура на базе CoreOS и KubernetesDenis Izmaylov
 
Remote (dev)tools своими руками
Remote (dev)tools своими рукамиRemote (dev)tools своими руками
Remote (dev)tools своими рукамиRoman Dvornov
 
Seminar: Эффективное использование среды разработки и компилятора C++
Seminar: Эффективное использование среды разработки и компилятора C++Seminar: Эффективное использование среды разработки и компилятора C++
Seminar: Эффективное использование среды разработки и компилятора C++Denis Vasilyev
 
Владимир Никонов "Вызовы при разработке enterprise продукта"
Владимир Никонов "Вызовы при разработке enterprise продукта"Владимир Никонов "Вызовы при разработке enterprise продукта"
Владимир Никонов "Вызовы при разработке enterprise продукта"Fwdays
 
Подходы и технологии, используемые в разработке iOS-клиента Viber, Кирилл Лаш...
Подходы и технологии, используемые в разработке iOS-клиента Viber, Кирилл Лаш...Подходы и технологии, используемые в разработке iOS-клиента Viber, Кирилл Лаш...
Подходы и технологии, используемые в разработке iOS-клиента Viber, Кирилл Лаш...Yandex
 
JavaScript-модули "из прошлого в будущее"
JavaScript-модули "из прошлого в будущее"JavaScript-модули "из прошлого в будущее"
JavaScript-модули "из прошлого в будущее"oelifantiev
 
C# Web. Занятие 11.
C# Web. Занятие 11.C# Web. Занятие 11.
C# Web. Занятие 11.Igor Shkulipa
 

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

Построение собственного JS SDK — зачем и как?
Построение собственного JS SDK — зачем и как?Построение собственного JS SDK — зачем и как?
Построение собственного JS SDK — зачем и как?
 
"Webpack: 7 бед — один ответ" — Денис Измайлов, MoscowJS 17
"Webpack: 7 бед — один ответ" — Денис Измайлов, MoscowJS 17"Webpack: 7 бед — один ответ" — Денис Измайлов, MoscowJS 17
"Webpack: 7 бед — один ответ" — Денис Измайлов, MoscowJS 17
 
Юрий Василевский "Автоматизация в XCode"
Юрий Василевский "Автоматизация в XCode"Юрий Василевский "Автоматизация в XCode"
Юрий Василевский "Автоматизация в XCode"
 
Юрий Василевский «Автоматизация в XCode»
Юрий Василевский «Автоматизация в XCode»Юрий Василевский «Автоматизация в XCode»
Юрий Василевский «Автоматизация в XCode»
 
IT-инфраструктура. FAQ для разработчика
IT-инфраструктура. FAQ для разработчикаIT-инфраструктура. FAQ для разработчика
IT-инфраструктура. FAQ для разработчика
 
И снова разработка под iOS. Павел Тайкало
И снова разработка под iOS. Павел ТайкалоИ снова разработка под iOS. Павел Тайкало
И снова разработка под iOS. Павел Тайкало
 
CQRS на практике. В поиске точки масштабирования и новых метафор
CQRS на практике. В поиске точки масштабирования и новых метафорCQRS на практике. В поиске точки масштабирования и новых метафор
CQRS на практике. В поиске точки масштабирования и новых метафор
 
Создаем Drupal дистрибутив: от идеи до сопровождения
Создаем Drupal дистрибутив: от идеи до сопровожденияСоздаем Drupal дистрибутив: от идеи до сопровождения
Создаем Drupal дистрибутив: от идеи до сопровождения
 
Непрерывная интеграция при разработке баз данных. (Show version)
Непрерывная интеграция при разработке баз данных. (Show version)Непрерывная интеграция при разработке баз данных. (Show version)
Непрерывная интеграция при разработке баз данных. (Show version)
 
Text
TextText
Text
 
#3 "Webpack и Vue.JS: Создание больших приложений и их расширение" Кирилл Кай...
#3 "Webpack и Vue.JS: Создание больших приложений и их расширение" Кирилл Кай...#3 "Webpack и Vue.JS: Создание больших приложений и их расширение" Кирилл Кай...
#3 "Webpack и Vue.JS: Создание больших приложений и их расширение" Кирилл Кай...
 
Aspect Oriented Approach
Aspect Oriented ApproachAspect Oriented Approach
Aspect Oriented Approach
 
Микросервисная архитектура на базе CoreOS и Kubernetes
Микросервисная архитектура на базе CoreOS и KubernetesМикросервисная архитектура на базе CoreOS и Kubernetes
Микросервисная архитектура на базе CoreOS и Kubernetes
 
Remote (dev)tools своими руками
Remote (dev)tools своими рукамиRemote (dev)tools своими руками
Remote (dev)tools своими руками
 
Seminar: Эффективное использование среды разработки и компилятора C++
Seminar: Эффективное использование среды разработки и компилятора C++Seminar: Эффективное использование среды разработки и компилятора C++
Seminar: Эффективное использование среды разработки и компилятора C++
 
Владимир Никонов "Вызовы при разработке enterprise продукта"
Владимир Никонов "Вызовы при разработке enterprise продукта"Владимир Никонов "Вызовы при разработке enterprise продукта"
Владимир Никонов "Вызовы при разработке enterprise продукта"
 
Continuous integration
Continuous integrationContinuous integration
Continuous integration
 
Подходы и технологии, используемые в разработке iOS-клиента Viber, Кирилл Лаш...
Подходы и технологии, используемые в разработке iOS-клиента Viber, Кирилл Лаш...Подходы и технологии, используемые в разработке iOS-клиента Viber, Кирилл Лаш...
Подходы и технологии, используемые в разработке iOS-клиента Viber, Кирилл Лаш...
 
JavaScript-модули "из прошлого в будущее"
JavaScript-модули "из прошлого в будущее"JavaScript-модули "из прошлого в будущее"
JavaScript-модули "из прошлого в будущее"
 
C# Web. Занятие 11.
C# Web. Занятие 11.C# Web. Занятие 11.
C# Web. Занятие 11.
 

More from Dev2Dev

D2D Чипец 2 Николай Иванов - Data Informed Design
D2D Чипец 2 Николай Иванов - Data Informed DesignD2D Чипец 2 Николай Иванов - Data Informed Design
D2D Чипец 2 Николай Иванов - Data Informed DesignDev2Dev
 
D2D Чипец 2 Сергей Ашмаров - Как из ничего сделать крутой фестивальный проект
D2D Чипец 2 Сергей Ашмаров - Как из ничего сделать крутой фестивальный проектD2D Чипец 2 Сергей Ашмаров - Как из ничего сделать крутой фестивальный проект
D2D Чипец 2 Сергей Ашмаров - Как из ничего сделать крутой фестивальный проектDev2Dev
 
D2D Чипец 2 Виталий Мазуревич - Engeneering design
D2D Чипец 2 Виталий Мазуревич - Engeneering designD2D Чипец 2 Виталий Мазуревич - Engeneering design
D2D Чипец 2 Виталий Мазуревич - Engeneering designDev2Dev
 
D2D Чипец 2 Николай Страх - Как студии параллельно с основной деятельностью з...
D2D Чипец 2 Николай Страх - Как студии параллельно с основной деятельностью з...D2D Чипец 2 Николай Страх - Как студии параллельно с основной деятельностью з...
D2D Чипец 2 Николай Страх - Как студии параллельно с основной деятельностью з...Dev2Dev
 
D2D Чипец 2 Алексей Резванов - "Особенности работы на международных рынках"
D2D Чипец 2 Алексей Резванов - "Особенности работы на международных рынках"D2D Чипец 2 Алексей Резванов - "Особенности работы на международных рынках"
D2D Чипец 2 Алексей Резванов - "Особенности работы на международных рынках"Dev2Dev
 
D2D Чипец 2 Максим Кулдошин - Как работать в Красноярске и не умереть со скуки
D2D Чипец 2 Максим Кулдошин - Как работать в Красноярске и не умереть со скукиD2D Чипец 2 Максим Кулдошин - Как работать в Красноярске и не умереть со скуки
D2D Чипец 2 Максим Кулдошин - Как работать в Красноярске и не умереть со скукиDev2Dev
 
D2D Чипец 2 Алексей Раменский - Видение рынка дизайна и проектирования от экс...
D2D Чипец 2 Алексей Раменский - Видение рынка дизайна и проектирования от экс...D2D Чипец 2 Алексей Раменский - Видение рынка дизайна и проектирования от экс...
D2D Чипец 2 Алексей Раменский - Видение рынка дизайна и проектирования от экс...Dev2Dev
 
D2D Pizza JS Роман Сальников "Redux: one state to rule them all"
D2D Pizza JS Роман Сальников "Redux: one state to rule them all"D2D Pizza JS Роман Сальников "Redux: one state to rule them all"
D2D Pizza JS Роман Сальников "Redux: one state to rule them all"Dev2Dev
 
D2D Pizza JS Илья Беда "Куда мы все катимся?"
D2D Pizza JS Илья Беда "Куда мы все катимся?"D2D Pizza JS Илья Беда "Куда мы все катимся?"
D2D Pizza JS Илья Беда "Куда мы все катимся?"Dev2Dev
 
D2D Pizza JS Игорь Ковган "Koa поможет"
D2D Pizza JS Игорь Ковган "Koa поможет"D2D Pizza JS Игорь Ковган "Koa поможет"
D2D Pizza JS Игорь Ковган "Koa поможет"Dev2Dev
 
D2D Pizza JS Владимир Кожин "Jii - фреймворк, который Вы уже знаете"
D2D Pizza JS Владимир Кожин "Jii - фреймворк, который Вы уже знаете"D2D Pizza JS Владимир Кожин "Jii - фреймворк, который Вы уже знаете"
D2D Pizza JS Владимир Кожин "Jii - фреймворк, который Вы уже знаете"Dev2Dev
 
D2D Pizza JS Тимофей Чаптыков "CSS-менеджмент в 2016"
D2D Pizza JS Тимофей Чаптыков "CSS-менеджмент в 2016"D2D Pizza JS Тимофей Чаптыков "CSS-менеджмент в 2016"
D2D Pizza JS Тимофей Чаптыков "CSS-менеджмент в 2016"Dev2Dev
 
Разработка приложений в Android studio
Разработка приложений в Android studioРазработка приложений в Android studio
Разработка приложений в Android studioDev2Dev
 
Мотивация ИТ-персонала - шаг за шагом - Марина Семехина, Илья Горбаров Dev2De...
Мотивация ИТ-персонала - шаг за шагом - Марина Семехина, Илья Горбаров Dev2De...Мотивация ИТ-персонала - шаг за шагом - Марина Семехина, Илья Горбаров Dev2De...
Мотивация ИТ-персонала - шаг за шагом - Марина Семехина, Илья Горбаров Dev2De...Dev2Dev
 
Линзы - комбинаторная манипуляция данными Александр Гранин Dev2Dev v2.0 30.05...
Линзы - комбинаторная манипуляция данными Александр Гранин Dev2Dev v2.0 30.05...Линзы - комбинаторная манипуляция данными Александр Гранин Dev2Dev v2.0 30.05...
Линзы - комбинаторная манипуляция данными Александр Гранин Dev2Dev v2.0 30.05...Dev2Dev
 
Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30...
Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30...Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30...
Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30...Dev2Dev
 
Good enough testing - Татьяна Писчасова Dev2Dev v2.0 30.05.2015
Good enough testing - Татьяна Писчасова Dev2Dev v2.0 30.05.2015Good enough testing - Татьяна Писчасова Dev2Dev v2.0 30.05.2015
Good enough testing - Татьяна Писчасова Dev2Dev v2.0 30.05.2015Dev2Dev
 
Failure story - Александр Ефремов Dev2Dev v1.5 23.11.2014
Failure story - Александр Ефремов Dev2Dev v1.5 23.11.2014Failure story - Александр Ефремов Dev2Dev v1.5 23.11.2014
Failure story - Александр Ефремов Dev2Dev v1.5 23.11.2014Dev2Dev
 
IT-инфраструктура - Александр Соболь Dev2Dev v1.5 23.11.2014
IT-инфраструктура - Александр Соболь Dev2Dev v1.5 23.11.2014IT-инфраструктура - Александр Соболь Dev2Dev v1.5 23.11.2014
IT-инфраструктура - Александр Соболь Dev2Dev v1.5 23.11.2014Dev2Dev
 

More from Dev2Dev (19)

D2D Чипец 2 Николай Иванов - Data Informed Design
D2D Чипец 2 Николай Иванов - Data Informed DesignD2D Чипец 2 Николай Иванов - Data Informed Design
D2D Чипец 2 Николай Иванов - Data Informed Design
 
D2D Чипец 2 Сергей Ашмаров - Как из ничего сделать крутой фестивальный проект
D2D Чипец 2 Сергей Ашмаров - Как из ничего сделать крутой фестивальный проектD2D Чипец 2 Сергей Ашмаров - Как из ничего сделать крутой фестивальный проект
D2D Чипец 2 Сергей Ашмаров - Как из ничего сделать крутой фестивальный проект
 
D2D Чипец 2 Виталий Мазуревич - Engeneering design
D2D Чипец 2 Виталий Мазуревич - Engeneering designD2D Чипец 2 Виталий Мазуревич - Engeneering design
D2D Чипец 2 Виталий Мазуревич - Engeneering design
 
D2D Чипец 2 Николай Страх - Как студии параллельно с основной деятельностью з...
D2D Чипец 2 Николай Страх - Как студии параллельно с основной деятельностью з...D2D Чипец 2 Николай Страх - Как студии параллельно с основной деятельностью з...
D2D Чипец 2 Николай Страх - Как студии параллельно с основной деятельностью з...
 
D2D Чипец 2 Алексей Резванов - "Особенности работы на международных рынках"
D2D Чипец 2 Алексей Резванов - "Особенности работы на международных рынках"D2D Чипец 2 Алексей Резванов - "Особенности работы на международных рынках"
D2D Чипец 2 Алексей Резванов - "Особенности работы на международных рынках"
 
D2D Чипец 2 Максим Кулдошин - Как работать в Красноярске и не умереть со скуки
D2D Чипец 2 Максим Кулдошин - Как работать в Красноярске и не умереть со скукиD2D Чипец 2 Максим Кулдошин - Как работать в Красноярске и не умереть со скуки
D2D Чипец 2 Максим Кулдошин - Как работать в Красноярске и не умереть со скуки
 
D2D Чипец 2 Алексей Раменский - Видение рынка дизайна и проектирования от экс...
D2D Чипец 2 Алексей Раменский - Видение рынка дизайна и проектирования от экс...D2D Чипец 2 Алексей Раменский - Видение рынка дизайна и проектирования от экс...
D2D Чипец 2 Алексей Раменский - Видение рынка дизайна и проектирования от экс...
 
D2D Pizza JS Роман Сальников "Redux: one state to rule them all"
D2D Pizza JS Роман Сальников "Redux: one state to rule them all"D2D Pizza JS Роман Сальников "Redux: one state to rule them all"
D2D Pizza JS Роман Сальников "Redux: one state to rule them all"
 
D2D Pizza JS Илья Беда "Куда мы все катимся?"
D2D Pizza JS Илья Беда "Куда мы все катимся?"D2D Pizza JS Илья Беда "Куда мы все катимся?"
D2D Pizza JS Илья Беда "Куда мы все катимся?"
 
D2D Pizza JS Игорь Ковган "Koa поможет"
D2D Pizza JS Игорь Ковган "Koa поможет"D2D Pizza JS Игорь Ковган "Koa поможет"
D2D Pizza JS Игорь Ковган "Koa поможет"
 
D2D Pizza JS Владимир Кожин "Jii - фреймворк, который Вы уже знаете"
D2D Pizza JS Владимир Кожин "Jii - фреймворк, который Вы уже знаете"D2D Pizza JS Владимир Кожин "Jii - фреймворк, который Вы уже знаете"
D2D Pizza JS Владимир Кожин "Jii - фреймворк, который Вы уже знаете"
 
D2D Pizza JS Тимофей Чаптыков "CSS-менеджмент в 2016"
D2D Pizza JS Тимофей Чаптыков "CSS-менеджмент в 2016"D2D Pizza JS Тимофей Чаптыков "CSS-менеджмент в 2016"
D2D Pizza JS Тимофей Чаптыков "CSS-менеджмент в 2016"
 
Разработка приложений в Android studio
Разработка приложений в Android studioРазработка приложений в Android studio
Разработка приложений в Android studio
 
Мотивация ИТ-персонала - шаг за шагом - Марина Семехина, Илья Горбаров Dev2De...
Мотивация ИТ-персонала - шаг за шагом - Марина Семехина, Илья Горбаров Dev2De...Мотивация ИТ-персонала - шаг за шагом - Марина Семехина, Илья Горбаров Dev2De...
Мотивация ИТ-персонала - шаг за шагом - Марина Семехина, Илья Горбаров Dev2De...
 
Линзы - комбинаторная манипуляция данными Александр Гранин Dev2Dev v2.0 30.05...
Линзы - комбинаторная манипуляция данными Александр Гранин Dev2Dev v2.0 30.05...Линзы - комбинаторная манипуляция данными Александр Гранин Dev2Dev v2.0 30.05...
Линзы - комбинаторная манипуляция данными Александр Гранин Dev2Dev v2.0 30.05...
 
Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30...
Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30...Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30...
Математическое обоснование SOLID принципов - Евгений Тюменцев Dev2Dev v2.0 30...
 
Good enough testing - Татьяна Писчасова Dev2Dev v2.0 30.05.2015
Good enough testing - Татьяна Писчасова Dev2Dev v2.0 30.05.2015Good enough testing - Татьяна Писчасова Dev2Dev v2.0 30.05.2015
Good enough testing - Татьяна Писчасова Dev2Dev v2.0 30.05.2015
 
Failure story - Александр Ефремов Dev2Dev v1.5 23.11.2014
Failure story - Александр Ефремов Dev2Dev v1.5 23.11.2014Failure story - Александр Ефремов Dev2Dev v1.5 23.11.2014
Failure story - Александр Ефремов Dev2Dev v1.5 23.11.2014
 
IT-инфраструктура - Александр Соболь Dev2Dev v1.5 23.11.2014
IT-инфраструктура - Александр Соболь Dev2Dev v1.5 23.11.2014IT-инфраструктура - Александр Соболь Dev2Dev v1.5 23.11.2014
IT-инфраструктура - Александр Соболь Dev2Dev v1.5 23.11.2014
 

Модульная структура. Цветцих Денис 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 Как это сделать – непонятно • Как устроен проект, позволяющий повторно использовать свои модули? • Как делить функционал на модули? • Как собрать проект из модулей?
  • 8. 8 Проект для одного заказчика UI Logic DataAccess Нельзя повторно использовать части, так как они зависят от нижних уровней
  • 9. 9 Inversion of Control • Модули верхнего уровня не должны зависеть от модулей нижнего уровня. И те, и другие должны зависеть от абстракции. • Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.
  • 10. 10 IoC проект UI Logic DataAccess interfaces Можно повторно использовать части системы Logic interfaces DataAccess
  • 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  • Проектировать плагины нужно сразу 
  • 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 class AutofacModule : Module { protected override void Load(ContainerBuilder builder) { // Регистрируем сервисы builder.RegisterType<Service>().As<IService>(); } }
  • 30. 30 Autofac: модуль public class AutofacModule : Module { protected override void Load(ContainerBuilder builder) { // Регистрируем сервисы builder.RegisterType<Service>().As<IService>(); } }
  • 31. 31 Autofac: Composition Root private IContainer Container; protected override void OnStartup(StartupEventArgs e) { var builder = new ContainerBuilder(); builder.RegisterModule<AutofacModule>(); Container = builder.Build(); }
  • 32. 32 Autofac: Composition Root private IContainer Container; protected override void OnStartup(StartupEventArgs e) { var builder = new ContainerBuilder(); builder.RegisterModule<AutofacModule>(); Container = builder.Build(); }
  • 33. 33 Autofac: Composition Root private IContainer 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 interface IService { } [Export(typeof(IService))] public class Service : IService { }
  • 37. 37 MEF: модуль public interface IService { } [Export(typeof(IService))] public class Service : IService { }
  • 38. 38 MEF: Composition Root var configuration = new ContainerConfiguration() .WithAssembly(typeof (IService).Assembly); var container = configuration.CreateContainer();
  • 39. 39 MEF: Composition Root var configuration = new ContainerConfiguration() .WithAssembly(typeof (IService).Assembly); var container = configuration.CreateContainer();
  • 40. 40 Особенности декларативного варианта Достоинства • Позволяет писать плагины • Порядок загрузки модулей произвольный Недостатки • Нельзя инкапсулировать реализации сервисов • Нельзя явно увидеть список сервисов модуля • Сложнее перейти на другой IoC контейнер • Загрузка может быть долгой
  • 41. 41 Пример: интернет - магазин Товары имеют вес и размеры (длина, ширина, высота) Корзина считает стоимость доставки товаров Базовый сценарий: расчет исходя из объема Кастомный сценарий: расчет исходя из веса Система реализована по принципу IoC  Нужно реализовать кастомный сценарий
  • 42. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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 internal class WeightCostCalculator : ICostCalculator { public int GetDeliveryCost(Product p) { return p.Weight; } }
  • 56. 56 Custom.Logic - WeightCostCalculator internal class WeightCostCalculator : ICostCalculator { public int GetDeliveryCost(Product p) { return p.Weight; } }
  • 57. 57 Custom.Logic - WeightCostCalculator internal class 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

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