Що не так з DI абстракцією ASP.NET Core?

Кілька місяців тому, коли ASP.NET Core був ще в RC1, я робив перші незграбні спроби перевести свій тестовий проект з MVC 5 на ASP.NET Core. На той момент в проекті вже використовувалася IOC бібліотека Simple Injector, і з цієї причини я хотів продовжувати використовувати цю бібліотеку, благо була підтримка з rc1. Я стежив за виходом нових версій цієї бібліотеки і відносно недавно натрапив на досить цікаву, на мій погляд, статтю, розміщену в тематичному блозі Simple Injector. Хоч стаття і спирається на відповідну бібліотеку, але основна її цінність у піднятті більш загальної проблеми — нової DI абстракції в ASP.NET Core.

Статья з блогу IOC бібліотеки Simple Injector
Автор Steve
Буду радий, якщо вкажете на помилки і неточності перекладу.


Останні кілька років Microsoft займалася розробкою нової версії платформи. NET: .NET Core. .NET Core — це повний редизайн існуючої платформи .NET, націлений на справжню кросплатформеність і сумісність з хмарними технологіями. Ми уважно стежили за розвитком .NET Core і випускали сумісні з платформою версії Simple Injector, починаючи з RC1. З випуском Simple Injector v3.2 ми офіційно підтримуємо .NET Core.

Як ви могли помітити, Microsoft додала свою власну DI бібліотеку в якості одного з основних компонентів фреймворка. Хтось може вигукнути «нарешті!». Відсутність такого компонента породило безліч опенсорсных DI бібліотек .NET. І Simple Injector, очевидно, один з них.

Не зрозумійте мене неправильно, ми аплодуємо Microsoft за просування DI як основний практики .NET, і це, ймовірно, призведе до появи ще більшої кількості розробників, практикуючих DI, що в свою чергу позитивно позначиться на нашій галузі. Проблема, однак, починається з абстракції, яку Microsoft визначила на вершині свого вбудованого DI контейнера. Порівняно з попередніми Resolve абстракціями (IDependencyResolver і IServiceProvider)нова абстракція додає Register API поверх IServiceCollection. Суть цієї абстракції для Microsoft в тому, що інші (більш функціонально багаті) DI бібліотеки можуть підключатися в платформу, в той час як розробники додатків сторонніх інструментів і фреймворків використовують стандартизовану абстракцію для реєстрації залежностей. Це дає розробникам додатків стандарт для інтеграції DI бібліотек на їх вибір.

На перший погляд може здатися, що мати таку абстракцію — хороша думка. Взагалі кажучи, в нашій галузі програмного забезпечення мало проблем, які не можуть бути вирішені шляхом додавання (додаткових) рівнів абстракції. Хоча в даному випадку міркування Microsoft помилкові. Експерти DI попереджали їх про цю проблему з самого початку, але безуспішно. Mark Seemann досить точно описав проблеми з цим підходом в цілому тут, де, на мій погляд, основні моменти його міркувань це:
  • Такий підхід тягне в напрямку найменшого спільного знаменника
  • Такий підхід пригнічує інновації
  • Такий підхід додає пекло версионирования
  • Стає складніше працювати, не використовуючи DI контейнер
  • Якщо розробкою адаптерів будуть займатися члени open-source спільноти, у цих адаптерів може бути різний рівень якості та вони можуть не бути сумісні з останньою версією Conforming Container(прим.пер. мається на увазі шаблон, описаний тут
Це реальні проблеми, що стоять перед нами сьогодні в новій DI абстракції .NET Core. DI контейнери часто мають дуже унікальні і несумісні особливо, коли мова заходить про їх registration API. Simple Injector, наприклад, дуже ретельно спроектований в області виявлення численних помилок конфігурації. Один з найяскравіших прикладів (а їх значно більше) — його діагностичні здібності. Це одна з особливостей, які докорінно несумісні з очікуваннями, які будуть у користувачів DI абстракції. А що ж будуть очікувати від користувачі нової абстракції?

Користувачів DI абстракції можна розділити на три групи: розробники фреймворків, зовнішніх бібліотек і самих додатків; особливо розробники фреймворків та зовнішніх бібліотек, які зараз замислюються над додаванням реєстрації своїх залежностей через загальну абстракцію. Так як для цих двох груп розробників практично неможливо перевірити їх код з усіма доступними адаптерами вони будуть тестувати свій код за допомогою вбудованого контейнера. І поки ці розробники використовують вбудований контейнер вони будуть (і, ймовірно, повинні) неявно очікувати стандартного поведінки від вбудованого контейнера — не важливо який адаптер використовується. Іншими словами, це вбудований контейнер визначає і контракт, і поведінка абстракції. Кожен створений адаптер повинен бути точним надмножеством вбудованого контейнера. Відхилення від норми не допускається, оскільки це порушило б роботу зовнішніх бібліотек, які залежать від поведінки за замовчуванням вбудованого контейнера.

Діагностика і верифікація в Simple Injector — одні з багатьох можливостей, що дозволяють вести розробку набагато продуктивніше. Вони дозволяють знаходити проблеми, які могли б бути виявлені набагато пізніше в процесі розробки, якщо б ви використовували інші DI бібліотеки. Але виконання діагностики та програми сторонніх компонент викличе проблеми — дуже малоймовірно, що сторонні компоненти будуть автоматично «грати за правилами» з діагностикою Simple Injector. Велика ймовірність, що вони будуть реєструвати залежно таким чином, при якому Simple Injector буде вважати їх підозрілими, навіть якщо вони (сподіваюся) добре протестували реєстрацію в особливих випадках зі стандартним контейнером. Гіпотетичному адаптера для Simple Injector було б неможливо розрізнити реєстрації сторонніх залежностей і залежностей програми. Відключення діагностики повністю прибере один з найважливіших запобіжних механізмів, в той час як збереження діагностики призведе до помилкових спрацьовувань з боку сторонніх компонентів, а ці помилкові спрацьовування доведеться придушувати розробникам програми. Оскільки реєстрація сторонніх компонент здебільшого прихована від розробників додатків, робота з усіма цими питаннями може виявитися складною, розчаровує і іноді навіть неможливою. Можна стверджувати — добре, що Simple Injector знаходить проблеми зі сторонніми інструментами. Але якщо ви захочете звернутися до розробників сторонніх бібліотек і спробуєте пояснити їм «проблему», то, ймовірно, вони переведуть стрілки на нас, адже «очевидно», що ми розробили «несумісний» адаптер.

Діагностичні здібності в Simple Injector — одні з багатьох несумісностей, з якими ми зіткнулися, коли писали адаптер для .NET Core DI абстракції. Інші несумісності:
Щоб зробити повністю сумісний адаптер для Simple Injector потрібно видалити багато відомих можливостей фреймворку, тим самим змінюючи існуючу поведінку бібліотеки і перетворюючи її в щось, що порушує принципи, якими ми керувалися при розробці. Непривабливе рішення. Мало того, що воно призведе до появи ламають сумісність змін, але воно так само пропадуть можливості і поведінку, за які Simple Injector і любили розробники. У цьому сенсі наявність адаптера — це «душение інновацій», як описував Mark. В Simple Injector ми зробили багато інновацій, а адаптер зробить Simple Injector практично марним для його користувачів. Адаптер так само обмежить нас від внесення подальших поліпшень і нововведень. Хтось може порахувати філософію Simple Injector радикальної, але ми думаємо інакше. Ми розробили його таким чином, який, як ми вважаємо, найкращим чином підійде для наших користувачів. кількість скачувань NuGet пакета вказує, що багато розробники згодні з нами. Відповідність певному адаптера буде заважати нам і надалі задовольняти наших користувачів.

Хоча бачення Simple Injector може відхилятися від норми більше, ніж більшість інших контейнерів, сам акт визначення загальної абстракції для майбутніх DI бібліотек — навіть більш радикальна або інноваційна точка зору, яка «душить інновації» майбутніх бібліотек. Тільки уявіть собі один з інших контейнерів, які впроваджують такі ж перевірки, які забезпечує Simple Injector? Така особливість не може бути введена без порушення контракту DI абстракції. Сам факт наявності такого адаптера може блокувати прогрес в нашій галузі.

Цим поясненням, я сподіваюся я так само прояснив, що Microsoft DI абстракція навіть не «найменший спільний знаменник», тому що «найменший спільний знаменник» передбачає сумісність з усіма DI бібліотеками. Як я висловився тут, є шанс, що ні одна з існуючих DI бібліотек не повністю сумісна з цією абстракцією. Частково це зводиться до того, що, хоча вбудований контейнер визначає контракт абстракції, проект з тестами цієї абстракції відчуває нестачу в солідній кількості тестових прикладів, які б повністю визначили точне поведінку у всіх сценаріях. Досі всі реалізації адаптера були спробою вгадати і сподіватися на краще, на те, що реалізація адаптера практично синхронізована з поведінкою вбудованого контейнера. Розробники Autofac наприклад, тільки що зрозуміли, що у них є деякі досить серйозні проблеми з сумісністю і в підсумку прийшли до тих же висновків і ми.

Це не було б так погано, якщо бібліотека DI Microsoft була багата можливостями і містила б такі функції, як верифікація та діагностика з Simple Injector. Тоді ми всі могли б використовували один і той же повнофункціональну DI бібліотеку. На жаль, реалізація далека не так функціонально багата, а сама Microsoft описала їх реалізацію

Мінімалістичний DI контейнер, корисний в тих випадках, коли вам не потрібні якісь додаткові можливості для ін'єкцій

Що ще гірше, з тих пір як вбудований контейнер визначає контракт абстракції, додавання нових можливостей у вбудований контейнер зламає всі існуючі адаптери! Будь-який сторонній розробник, що використовує абстракцію, буде тестувати (бібліотеки) тільки за допомогою вбудованої бібліотеки.NET Core's DI). А коли бібліотека стороннього розробника починає залежати від якоїсь функції, доданої у вбудований контейнер, і який при цьому ще не підтримується адаптером, щось зламається і постраждає розробник програми. Це один з аспектів пекла версионирования, який Mark Seemann обговорює в своєму блозі. Будемо сподіватися, що, принаймні, Microsoft буде збільшувати основний номер версії (major version number) кожен раз, коли вони будуть вносити зміни. Мало того, що їх поточна реалізація «мінімалістична», вона ніколи не зможе розвиватися повністю придатний багатофункціональний DI контейнер, тому що вони загнали себе в кут: кожне майбутнє зміна — це зміна, що ламають сумісність, від якого всім буде погано.

Краще рішення — уникати використання абстракції та її адаптерів повністю. Mark Seemann досить точно пояснив тут і тут, бібліотекам і фреймворкам, можливо, не треба використовувати DI контейнер взагалі. На жаль, сам факт визначення абстракції набагато ускладнює спробу уникнути її використання. Визначаючи абстракцію і активно просуваючи її використання, Microsoft призводить тисячі сторонніх розробників бібліотек та фреймворків до того, щоб вони перестати думати про визначення правильної абстракції для бібліотеки і фреймворку (статті Mark Seemann ясно описують це). Розробники більше не думають про це, тому що Microsoft змушує їх вірити, що весь світ потребує однієї загальної абстракції. Ми вже бачили, як нові фабричні інтерфейси для MVC вступали в гру дуже пізно (наприклад, IViewComponentActivator абстракції до початку RC2). І якщо ми бачимо, що команда MVC доводить такі помилки до такого пізнього етапу циклу розробки, що ми можемо очікувати від всіх тих розробників, які починають розробляти на новій платформі .NET?

Висновок

Визначення DI абстракції — болюча помилка Microsoft, яка буде переслідувати нас протягом багатьох наступних років. Вона вже пригнічує інновації, породжує пекло версионирования і засмучує багатьох розробників. Абстракція несумісна з багатьма бібліотеками DI і, всупереч рекомендації експертів, Microsoft вирішила зберегти її, поділяючи світ на несумісні і частково сумісні контейнери, що призводить до нескінченних повідомленнями про проблеми адаптерів, що реалізують DI абстракцію і сторонніми бібліотеками, які використовують цю абстракцію.

На наш погляд, як розробник програми, ви повинні утримуватися від використання адаптера і в статті я розповім більш докладно, як підійти до цього і чому, навіть без несумісного контейнера, це надійний шлях вперед.

Будьте на зв'язку.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.