Стандартный и нестандартные DI контейнеры

Содержание

Слайд 2

Меня хорошо видно && слышно?

Ставьте , если все хорошо
Напишите в чат, если

Меня хорошо видно && слышно? Ставьте , если все хорошо Напишите в
есть проблемы

+

Слайд 3

Стандартный и нестандартные DI контейнеры в ASP.NET Core

Гранковский Андрей

Архитектор направления
Альфа-Банк

https://www.linkedin.com/in/agrankovskiy/

Стандартный и нестандартные DI контейнеры в ASP.NET Core Гранковский Андрей Архитектор направления Альфа-Банк https://www.linkedin.com/in/agrankovskiy/

Слайд 4

Гранковский Андрей
8 лет опыта в разработке программного обеспечения и из них последние

Гранковский Андрей 8 лет опыта в разработке программного обеспечения и из них
6 лет в качестве .NET разработчика, в том числе, как Full-stack разработчик.
В 2014 году закончил МГТУ им. Н.Э. Баумана
Работал в таких компаниях, как Райффайзенбанк, ЦИАН, Локо-банк
Имею сертификаты MCP, MCSD: Programming in C#
Люблю разработку на C#, архитектуру, DDD, тестирование и Agile, стараюсь ориентироваться, как в backend, так и во frontend разработке

Преподаватель

Слайд 5

Активно участвуем
Задаем вопрос в чат
Вопросы вижу в чате, могу ответить не сразу

Активно участвуем Задаем вопрос в чат Вопросы вижу в чате, могу ответить не сразу Правила вебинара

Правила вебинара

Слайд 6

Цели вебинара

1

Повторить преимущества DI/IOC принципа и основные возможности DI-контейнера для ASP.NET Core

2

Изучить

Цели вебинара 1 Повторить преимущества DI/IOC принципа и основные возможности DI-контейнера для
жизненный цикл объектов в DI -контейнере

3

Изучить способы конфигурации нестандартных DI контейнеров и дополнительные инструменты

Слайд 7

Смысл | Зачем вам это уметь

DI - контейнеры - важнейший механизм

Смысл | Зачем вам это уметь DI - контейнеры - важнейший механизм
для построения расширяемой архитектуры Web-приложений

1

2

Стандартный DI контейнер подходит для большей части проектов и активно используется

3

Для проектов, где нужны продвинутые инструменты могут понадобиться другие контейнеры

Слайд 8

Маршрут вебинара

Best Practices/DI/IOC

DI-контейнер ASP.NET Core

Жизненный цикл объектов в DI-контейнере

Нестандартные DI- контейнеры и

Маршрут вебинара Best Practices/DI/IOC DI-контейнер ASP.NET Core Жизненный цикл объектов в DI-контейнере
расширения

Слайд 9

Репозиторий с примером

Репозиторий с проектом для занятия, кому удобнее смотреть у себя

Репозиторий с примером Репозиторий с проектом для занятия, кому удобнее смотреть у
- клонируем
https://gitlab.com/devgrav/otus.teaching.promocodefactory.demo.di
Напишите в чат +, если репозиторий доступен

Тайминг: 1 минута

Слайд 11

Маршрут вебинара

Best Practices/DI/IOC

DI-контейнер ASP.NET Core

Жизненный цикл объектов в DI-контейнере

Нестандартные DI- контейнеры и

Маршрут вебинара Best Practices/DI/IOC DI-контейнер ASP.NET Core Жизненный цикл объектов в DI-контейнере
расширения

Слайд 12

Best Practices

Best Practices

Слайд 13

Вопрос

Кто уже делал дополнительное задание про Employees CRUD в первом ДЗ?
Напишите в

Вопрос Кто уже делал дополнительное задание про Employees CRUD в первом ДЗ?
чат + или -

Тайминг: 1 минута

Слайд 14

Вопрос

Как вы считаете много ли кода приходится на заполнение и маппинг данных

Вопрос Как вы считаете много ли кода приходится на заполнение и маппинг
из одних объектов в другие, например из Models в Domain Entity и наоборот?
Напишите в чат сколько это в процентах по вашему мнению 20%, 30% и т.д.

Тайминг: 1 минута

Слайд 15

Минутка Best Practices

Такого кода очень много, многие операции бизнес-логики сводятся к простому

Минутка Best Practices Такого кода очень много, многие операции бизнес-логики сводятся к
маппингу в существующие или новые объекты.
Использование инициализаторов ведет к ошибкам, так как нарушает инкапсуляцию создания и изменения объекта
Субъективно > 50% ошибок вызвано ошибками в Create/Update операциях из-за копипаста, контроллеры и сервисы получаются “толстыми” - в итоге много плохого кода

Слайд 16

Минутка Best Practices

Стараемся выносить маппинг и создание объектов в отдельные компоненты (Мапперы,

Минутка Best Practices Стараемся выносить маппинг и создание объектов в отдельные компоненты
Фабрики) и/или использовать конструкторы сущностей/агрегатов

Слайд 17

Инициализация и мапперы

Инициализация и мапперы

Слайд 18

Инициализация и мапперы

Плюсы:
Лучше Single Responsibility;
Соблюдаем инкапсуляцию при создании объектов;
Меньше багов
Легче покрыть unit-тестами
Код

Инициализация и мапперы Плюсы: Лучше Single Responsibility; Соблюдаем инкапсуляцию при создании объектов;
бизнес-логики становится читаемее в разы, в итоге лучше поддержка
Минусы
Иногда можем смешивать операции создания/обновления, тогда используем конструкторы и отдельные Edit методы внутри класса сущности/агрегата или специальную фабрику
Если все делать правильно, то нужно использовать классы-зависимости, например, IEmployeeFactory, в итоге много компонентов, но можно обойтись статическими классами для этого, обычно их достаточно

Слайд 19

Про Automapper

Эти проблемы частично решает Automapper, но обычно в сторону простых моделей

Про Automapper Эти проблемы частично решает Automapper, но обычно в сторону простых
от Entities, плюс создаем сильную связь с этой библиотекой, но в целом вариант хороший

Слайд 20

Инициализация и мапперы

https://gitlab.com/devgrav/otus.teaching.promocodefactory.demo.di
EmployeesBestPracticesController

Инициализация и мапперы https://gitlab.com/devgrav/otus.teaching.promocodefactory.demo.di EmployeesBestPracticesController

Слайд 22

Вопрос

Тайминг: 2 минуты

Что вообще такое зависимость класса?
Напишите в чат или -, если

Вопрос Тайминг: 2 минуты Что вообще такое зависимость класса? Напишите в чат
нужно пояснить

Слайд 23

Зависимости классов

Зависимость — это любой объект, который требуется другому объекту.

Зависимости классов Зависимость — это любой объект, который требуется другому объекту.

Слайд 24

Зависимости классов

Зависимость

Зависимость

Зависимость

Зависимости классов Зависимость Зависимость Зависимость

Слайд 25

Вопрос

Какие есть проблемы/преимущества у данных вариантов зависимостей?
Напишите в чат по каждому виду:

Вопрос Какие есть проблемы/преимущества у данных вариантов зависимостей? Напишите в чат по
цифра - пояснение

1

2

3

Слайд 26

Пример проблем с зависимостями
из жизни

Пример проблем с зависимостями из жизни

Слайд 27

Пример проблем с зависимостями
из жизни

Есть Web-приложение, в нем есть функция генерации .pdf

Пример проблем с зависимостями из жизни Есть Web-приложение, в нем есть функция
файла отчета на основе отчета MS SQL Reporting Service
Пользователь в меню приложения выбирает отчет и настраивает входные параметры;
Идет вызов ReportController/МетодДляНужногоОтчета
Там происходит сбор входных параметров и передача их в MS SQL Reporting Service, конфиг для доступа лежит в web.config. Доступ к серверу отчетов сделан в статическом классе;
Отчетов более 300 штук, в каждом методе вызывается статический класс
Появляется задача перенести эти настройки в БД, в приложении настройки уже везде пробрасываются через DI;
Как итог надо переписать 300 методов, чтобы перевести работу с настройками на объект из контейнера и заменить статический класс обычным, можно было это сделать заранее, а не писать статический класс, чтобы сделать быстро;

Слайд 28

Dependency Injection

Чтобы явно знать от каких классов зависит другой класс мы

Dependency Injection Чтобы явно знать от каких классов зависит другой класс мы
используем инъекции в конструктор, мы точно знаем, что нужно классу для работы, а объявить эти зависимости попросим другой класс - Composition Root, можно задействовать полиморфизм, увеличим гибкость программы, сделать ее модульной

Слайд 29

SOLID

S - Single Responsibility principle
O - Open/Closed principle
L - Liskov substitution principle
I

SOLID S - Single Responsibility principle O - Open/Closed principle L -
- Interface segregation principle
D - Dependency inversion principle

Слайд 30

Вопрос

В чем отличие Dependency Injection и почему говорят еще про Dependency Inversion

Вопрос В чем отличие Dependency Injection и почему говорят еще про Dependency
и Inversion of control?
Напишите в чат или -, +-, если кажется, что это одно и то же

Тайминг: 3 минуты

Слайд 31

Многоуровневая и гексагональная архитектура

В чем разница у этих архитектур с точки зрения

Многоуровневая и гексагональная архитектура В чем разница у этих архитектур с точки
DI/IOC или ее нет, ведь и там и там интерфейсы можно подкладывать?
Напишите в чат или -

Слайд 32

DI/IOC

Формулировка:
Модули верхних уровней не должны зависеть от модулей нижних уровней. Оба типа

DI/IOC Формулировка: Модули верхних уровней не должны зависеть от модулей нижних уровней.
модулей должны зависеть от абстракций.
Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.
Зависимости идут в направлении противоположному потоку управления (Поэтому Inversion Of Control)

Слайд 33

Контейнер

Контейнер зависимостей (DI-контейнер) - это только инструмент создания/жизненного цикла и инъекции зависимостей,

Контейнер Контейнер зависимостей (DI-контейнер) - это только инструмент создания/жизненного цикла и инъекции
он ничего не знает про IOC, если конечно мы не имеем в виду под этим перенос инициализации зависимостей в Composition Root
Будет ли наша архитектура соблюдать IOC зависит от нас
IOC - про связи компонентов, а не про контейнеры

Слайд 34

Гексагональная архитектура

Port

Port

Port

Port

Port

Port

Domain Entities

Application Services /Use Cases

ASP.NET Core Web Api

Infrastructure/ Data Access/ MongoDb

Вызываем

Гексагональная архитектура Port Port Port Port Port Port Domain Entities Application Services
API
GET api/users/1

Вызываем метод GetUserById(1) интерфейса IUserUseCasesService

Вызываем метод FindUserById(1) интерфейса IUserRepository

Интерфейсы определены в Core

Запрос идет сверху - вниз, от UI до реализации репозитория, но бизнес-логика использует интерфейс, который определен ниже;
Зависимости “снаружи-внутри”, хотя поток выполнения программы идет как обычно

Нашли пользователя в БД и вернули ответ

Слайд 35

DI-контейнер ASP.NET Core

DI-контейнер ASP.NET Core

Слайд 36

Маршрут вебинара

Best Practices/DI/IOC

DI-контейнер ASP.NET Core

Жизненный цикл объектов в DI-контейнере

Нестандартные DI- контейнеры и

Маршрут вебинара Best Practices/DI/IOC DI-контейнер ASP.NET Core Жизненный цикл объектов в DI-контейнере
расширения

Слайд 37

ASP.NET Core и DI

DI - это основа архитектуры ASP.NET Core и отличие

ASP.NET Core и DI DI - это основа архитектуры ASP.NET Core и
от предыдущего ASP.NET, так как любой элемент внутренней инфраструктуры может быть изменен, как и пользовательские компоненты.
Реализация находится в пакете Microsoft.Extensions.DependencyInjection

Слайд 38

Возможности DI-контейнера ASP.NET Core

Встроенный контейнер зависимостей предназначен для платформы ASP.NET Core и

Возможности DI-контейнера ASP.NET Core Встроенный контейнер зависимостей предназначен для платформы ASP.NET Core
большинства клиентских приложений;
Это фактически самый быстрый контейнер в .NET
Встроенный контейнер поддерживает основные инструменты, которые нужны:
Внедрение в конструкторы
Использование реализации по умолчанию и Generic-реализации
Управление временем жизни объекта (3 основных режима) и Scope объекта
Легкие инструменты расширения и замены контейнера
Внедрение, как платформенных служб, так и клиентских
Абстракции контейнера - это основа гибкой архитектуры ASP.NET Core

Слайд 39

Возможности DI-контейнера ASP.NET Core

Контейнер не поддерживает функции, которые на самом деле не

Возможности DI-контейнера ASP.NET Core Контейнер не поддерживает функции, которые на самом деле
нужны для большинства приложений:
Инъекции в свойство;
Подконтейнеры и другие средства реализации плагинной архитектуры;
Динамический резолв зависимостей по соглашению (вот это полезная фича)
Некоторые другие функции…
Он поддерживает большинство функций, которые нужны для микросервисов и средних приложений

Слайд 40

Где конфигурируем зависимости?

IServiceCollection - основная абстракция для работы с сервисами, которые хотим

Где конфигурируем зависимости? IServiceCollection - основная абстракция для работы с сервисами, которые
зарегистрировать в контейнере
Вызов конфигурации происходит при старте приложения, зависимости разрешаются на каждый запрос в зависимости от жизненного цикла объекта

Слайд 41

Основные методы и сущности

IServiceCollection
(Список сущностей)

BuildServiceProvider
(Фиксируем зависимости)

IServiceProvider.GetService
(Получаем зависимость)

Идемпотентные
(в основном для библиотек)

Неидемпотентные

Основные методы и сущности IServiceCollection (Список сущностей) BuildServiceProvider (Фиксируем зависимости) IServiceProvider.GetService (Получаем

Слайд 42

Как работать с контейнером изолированно

https://gitlab.com/devgrav/otus.teaching.promocodefactory.demo.di

Как работать с контейнером изолированно https://gitlab.com/devgrav/otus.teaching.promocodefactory.demo.di

Слайд 43

Жизненный цикл объектов в DI-контейнере

Жизненный цикл объектов в DI-контейнере

Слайд 44

Маршрут вебинара

Best Practices/DI/IOC

DI-контейнер ASP.NET Core

Жизненный цикл объектов в DI-контейнере

Нестандартные DI- контейнеры и

Маршрут вебинара Best Practices/DI/IOC DI-контейнер ASP.NET Core Жизненный цикл объектов в DI-контейнере
расширения

Слайд 45

Жизненный цикл объектов в DI-контейнере

Scoped

Transient

Singleton

Три вида жизненного цикла зависимостей

Жизненный цикл объектов в DI-контейнере Scoped Transient Singleton Три вида жизненного цикла зависимостей

Слайд 46

Вопрос

Тайминг: 2 минуты

Что такое Transient и зачем нам может понадобиться Transient зависимость?
Напишите

Вопрос Тайминг: 2 минуты Что такое Transient и зачем нам может понадобиться
в чат или -, если надо пояснить

Слайд 47

Transient в ASP.NET Core

Зависимость создается каждый раз, когда она нам нужна, хорошо

Transient в ASP.NET Core Зависимость создается каждый раз, когда она нам нужна,
подходит для Stateless компонентов и если есть многопоточность

Слайд 48

Transient зависимости

https://gitlab.com/devgrav/otus.teaching.promocodefactory.demo.di

Transient зависимости https://gitlab.com/devgrav/otus.teaching.promocodefactory.demo.di

Слайд 49

Singleton в ASP.NET Core

Один экземпляр на все запросы, то есть будет создан

Singleton в ASP.NET Core Один экземпляр на все запросы, то есть будет
один раз, не очень при многопоточной работе, у него не должно быть изменяемого состояния
Нельзя использовать со Scoped

Слайд 50

Singleton зависимости

https://gitlab.com/devgrav/otus.teaching.promocodefactory.demo.di

Singleton зависимости https://gitlab.com/devgrav/otus.teaching.promocodefactory.demo.di

Слайд 51

Вопрос

Тайминг: 2 минуты

Как работает Scoped для ASP.NET Core?
Напишите в чат или -,

Вопрос Тайминг: 2 минуты Как работает Scoped для ASP.NET Core? Напишите в
если надо пояснить

Слайд 52

Scoped в ASP.NET Core

Основное, что надо знать про Scoped в ASP.NET Core:

Scoped в ASP.NET Core Основное, что надо знать про Scoped в ASP.NET

Будет создан один экземпляр каждой зависимости пока не закончился запрос, то есть мы не вернули ответ клиенту
Все Disposed объекты будут жить до конца Scope

Слайд 53

Как создаем Scope

https://gitlab.com/devgrav/otus.teaching.promocodefactory.demo.di

Как создаем Scope https://gitlab.com/devgrav/otus.teaching.promocodefactory.demo.di

Слайд 54

Зачем создавать новый Scope?

Например, если все зависимости в рамках запроса разрешены, как

Зачем создавать новый Scope? Например, если все зависимости в рамках запроса разрешены,
Scoped, а нам нужна новая, иногда такое нужно при работе с EF, так как DbContext обычно существует на запрос, чтобы фиксировать транзакции в рамках запроса
Если нужен новый DbContext, то может понадобиться создать Scope, но этого лучше не делать, так как это инфраструктурный код

Слайд 55

Про жизненный цикл

При старте приложения ASP.NET Core собираем провайдер
Провайдер будет существовать пока

Про жизненный цикл При старте приложения ASP.NET Core собираем провайдер Провайдер будет
не остановим приложение
То есть все Singleton зависимости будут существовать до остановки приложения, поэтому он IDisposable
Для Scoped объекты привязаны к запросу или using scope, в итоге будут собраны и вызван Dispose

Слайд 56

Нестандартные DI-контейнеры и расширения

Нестандартные DI-контейнеры и расширения

Слайд 57

Маршрут вебинара

Best Practices/DI/IOC

DI-контейнер ASP.NET Core

Жизненный цикл объектов в DI-контейнере

Нестандартные DI- контейнеры и

Маршрут вебинара Best Practices/DI/IOC DI-контейнер ASP.NET Core Жизненный цикл объектов в DI-контейнере
расширения

Слайд 58

Зачем менять DI контейнер в ASP.NET Core

Инъекция в свойство;
Инъекция по имени;
Дочерние контейнеры;
Настраиваемое

Зачем менять DI контейнер в ASP.NET Core Инъекция в свойство; Инъекция по
управление временем существования;
Регистрация на основе соглашения;
Регистрация с помощью модулей, когда вы можете указать класс, который инкапсулирует конфигурацию
.

Слайд 59

DI контейнеры в .NET

Autofac
Castle Windsor
Lamar
LightInject
Ninject
SimpleInjector
Spring.NET
Unity
LinFu (inactive)
Managed Extensibility Framework (MEF) (abandoned / deprecated)
PicoContainer.NET

DI контейнеры в .NET Autofac Castle Windsor Lamar LightInject Ninject SimpleInjector Spring.NET
(abandoned / deprecated)
S2Container.NET (abandoned / deprecated)
StructureMap (abandoned / deprecated)

https://www.claudiobernasconi.ch/2019/01/24/the-ultimate-list-of-net-dependency-injection-frameworks/

Слайд 60

Сравнение контейнеров

Можно посмотреть по ссылке:
https://danielpalme.github.io/IocPerformance

Если кратко
Autofac достаточно производительный и является одним

Сравнение контейнеров Можно посмотреть по ссылке: https://danielpalme.github.io/IocPerformance Если кратко Autofac достаточно производительный
из самых популярных для ASP.NET MVC и совместим с ASP.NET Core, у него хорошая документация и поддержка
SimpleInjector также достаточно популярен и совместим с ASP.NET Core
Ninject, Castle Windsdor и Unity уже нет особого смысла рассматривать
Но они все равно проигрывают по производительности стандартному контейнеру Microsoft.Extension.DependencyInjection

https://habr.com/ru/post/302240/

Слайд 61

Контейнер Autofac

Очень популярен для ASP.NET MVC и имеет хорошую интеграцию с Core

Контейнер Autofac Очень популярен для ASP.NET MVC и имеет хорошую интеграцию с
и документацию;
Есть регистрация модулей;
Есть инъекция в свойство;
Можно делать подконтейнеры;

Слайд 62

Подключаем Autofac

https://gitlab.com/devgrav/otus.teaching.promocodefactory.demo.di

Подключаем Autofac https://gitlab.com/devgrav/otus.teaching.promocodefactory.demo.di

Слайд 63

Динамическое разрешение зависимостей через модули

https://gitlab.com/devgrav/otus.teaching.promocodefactory.demo.di

Динамическое разрешение зависимостей через модули https://gitlab.com/devgrav/otus.teaching.promocodefactory.demo.di

Слайд 64

Вопрос

Тайминг: 2 минуты

Зачем нам может понадобиться инъекция в свойство?
Напишите в чат или

Вопрос Тайминг: 2 минуты Зачем нам может понадобиться инъекция в свойство? Напишите
-, если надо пояснить

Слайд 65

Инъекция в свойство
Может быть полезно если у нас есть базовый контроллер, который

Инъекция в свойство Может быть полезно если у нас есть базовый контроллер,
написали сами и контроллеры, от которых он наследует, чтобы не менять конструкторы всех наследуемых контроллеров можно какой-то параметр внедрить через свойство

Слайд 66

Инъекция в свойство

https://gitlab.com/devgrav/otus.teaching.promocodefactory.demo.di

Инъекция в свойство https://gitlab.com/devgrav/otus.teaching.promocodefactory.demo.di

Слайд 67

Динамическое разрешение зависимостей через фабрику

Если мы уже собрали контейнер, то в него

Динамическое разрешение зависимостей через фабрику Если мы уже собрали контейнер, то в
просто так не добавить зависимости, допустим нужно выбрать реализацию в Runtime, либо вспоминаем про ServiceLocator, что не очень хорошо, либо пишем свою фабрику и через нее используем контейнер
Например, нужно построить дерево зависимостей по параметру запроса

Слайд 68

Динамическое разрешение зависимостей через фабрику

https://gitlab.com/devgrav/otus.teaching.promocodefactory.demo.di

Попробуйте по этим ссылкам настроить сами после занятия
https://stackoverflow.com/questions/54127414/using-factory-pattern-with-asp-net-core-dependency-injection
https://espressocoder.com/2018/10/08/injecting-a-factory-service-in-asp-net-core/

Динамическое разрешение зависимостей через фабрику https://gitlab.com/devgrav/otus.teaching.promocodefactory.demo.di Попробуйте по этим ссылкам настроить сами после занятия https://stackoverflow.com/questions/54127414/using-factory-pattern-with-asp-net-core-dependency-injection https://espressocoder.com/2018/10/08/injecting-a-factory-service-in-asp-net-core/

Слайд 69

Конфигурация по соглашению

На самостоятельную проработку
Чтобы не писать каждый раз Add каждого сервиса

Конфигурация по соглашению На самостоятельную проработку Чтобы не писать каждый раз Add
было бы удобно регистрировать по соглашению о наименовании, например, для всех классов, заканчивающихся на Service добавлять все одной строчкой или использующих Marker интерфейс

Слайд 70

Конфигурация по соглашению

Можно использовать специальный контейнер вместо стандартного, но это может быть

Конфигурация по соглашению Можно использовать специальный контейнер вместо стандартного, но это может
тяжелое решение, поэтому есть библиотека Scrutor, которая не вносит особых изменений в инфраструктуру, но добавляет фичи по динамической регистрации зависимостей
Это может быть актуально для CQRS подхода, где нам нужно регистрировать много обработчиков команд, также для этого полезна библиотека Mediatr, но это только часть ее назначения

Слайд 71

Конфигурация по соглашению

Можно посмотреть в документации Autofac
https://autofaccn.readthedocs.io/en/latest/register/scanning.html

Конфигурация по соглашению Можно посмотреть в документации Autofac https://autofaccn.readthedocs.io/en/latest/register/scanning.html

Слайд 72

Scrutor

https://andrewlock.net/using-scrutor-to-automatically-register-your-services-with-the-asp-net-core-di-container/

https://github.com/khellang/Scrutor

Удобное расширение для ASP.NET Core контейнера DI

Scrutor https://andrewlock.net/using-scrutor-to-automatically-register-your-services-with-the-asp-net-core-di-container/ https://github.com/khellang/Scrutor Удобное расширение для ASP.NET Core контейнера DI

Слайд 73

Выводы


1

2

3

Повторить преимущества DI/IOC принципа и основные возможности DI-контейнера для ASP.NET Core

Изучили жизненный

Выводы 1 2 3 Повторить преимущества DI/IOC принципа и основные возможности DI-контейнера
цикл объектов в DI -контейнере

Изучить способы конфигурации нестандартных DI контейнеров и дополнительные инструменты

Слайд 74

Список материалов для изучения

Внедрение зависимостей в .NET. Марк Симан
https://www.ozon.ru/context/detail/id/22104901/

Чистая архитектура. Роберт Мартин
https://www.ozon.ru/context/detail/id/144499396/

Список материалов для изучения Внедрение зависимостей в .NET. Марк Симан https://www.ozon.ru/context/detail/id/22104901/ Чистая архитектура. Роберт Мартин https://www.ozon.ru/context/detail/id/144499396/

Слайд 75

Заполните, пожалуйста,
опрос о занятии по ссылке
https://otus.ru/polls/15890/
Лучше всего написать что-то текстом!)

Заполните, пожалуйста, опрос о занятии по ссылке https://otus.ru/polls/15890/ Лучше всего написать что-то текстом!)