.NET Разработчик
6.51K subscribers
427 photos
2 videos
14 files
2.04K links
Дневник сертифицированного .NET разработчика.

Для связи: @SBenzenko

Поддержать канал:
- https://boosty.to/netdeveloperdiary
- https://patreon.com/user?u=52551826
- https://pay.cloudtips.ru/p/70df3b3b
Download Telegram
Конференция по .NET-разработке DotNext ищет спикеров 🎙

Вам есть о чем рассказать и что обсудить с коллегами по цеху? Тогда вам нужно подать заявку на участие в конференции!

Осенью DotNext пройдет 18-21 октября, онлайн (гибридный формат решили отложить из-за непредсказуемости ввода ограничений на офлайн мероприятия).

Темы, которые ждут больше всего:
✔️ Internals;
✔️ Architecture;
✔️ Security;
✔️ Performance;
✔️ DevOps & Tools.

Но не ограничивайте себя этим списком — вы можете подать заявку с любой темой из области .NET-разработки.

Если все-таки сомневаетесь, то программный комитет всегда готов обсудить актуальность темы и помочь выбрать правильный вектор доклада. Плюс, ребята помогут с прокачкой ваших ораторских навыков, если у вас мало опыта в публичных выступлениях.

Подать заявку и узнать подробности можно на сайте.
Вопросы присылайте на почту [email protected]
День восемьсот восемьдесят восьмой. #TypesAndLanguages
4. Проблема Алмаза. Начало
Проблема Алмаза, иногда называемая Смертельным Алмазом Смерти, возникает, когда мы наследуем одно и то же через несколько базовых сущностей. Если вы думаете, что «проблема есть в C++, но её не существует в Java или C#», то вы слишком сосредотачиваетесь на технической части.

Наследование
Обычно мы говорим, что в Java или C# существует одиночное наследование и множественная реализация интерфейсов. Это правда, но за этим скрывается гораздо более широкая картина.

Наследование позволяет наследовать характеристики и функции от базовой сущности (в большинстве случаев от класса или объекта). Есть много вещей, которые мы можем унаследовать, или много уровней наследования:

1. Наследование сигнатуры
В интерфейсе объявлен метод, мы наследуем его и предоставляем реализацию. Сигнатура здесь означает, что это только «заголовок» метода, без тела. Важно понимать, что эта «сигнатура наследования» не обязательно должна совпадать с «сигнатурой вызова». Например, вы не можете изменить тип возвращаемого значения при реализации интерфейса в C#, но тип возвращаемого значения не является частью «сигнатуры вызова». Java допускает это, но это детали реализации. Когда мы говорим о «наследовании сигнатуры», мы имеем в виду только заголовок метода, который мы получаем от базовой сущности.

2. Наследование реализации
При наследовании реализации мы получаем не только сигнатуру, но и всё тело метода. Это не так давно стало доступно в Java или C# через реализации интерфейса по умолчанию. Это можно рассматривать как типажи (traits). И хотя между наследованием реализации и трейтами есть некоторые различия, они довольно близки друг к другу.

3. Наследование состояния
Это наследование полей. При наследовании состояния мы получаем поле из базовой сущности, которое мы можем использовать в подклассе. В некоторой степени это похоже на примеси (mixins). Также стоит отметить, что у нас может быть наследование состояния без наследования реализации, но в большинстве случаев эти два аспекта объединяются.

4. Наследование идентичности
Можно считать «наследованием конструктора» (не вдаваясь в теорию типов). Разница между миксином и наследованием от класса сводится к конструктору. Вы можете создать экземпляр и получить новую идентичность. Обычно мы получаем идентичность, создавая базовую сущность и «удерживая» её внутри подобъекта.

Наследование в C# и Java
C++ имеет множественное наследование и не делает различий между классом и интерфейсом. В C# и Java запрещено всё, кроме наследования сигнатур. Однако важно понимать, что утверждение, что «в C# и Java не существует множественного наследования», неверно. Существует множественное наследование для сигнатур и одиночное наследование для всего остального.

Однако позже создатели языков поняли, что это может быть не лучшая идея, и была добавлена реализация интерфейса по умолчанию, которая по сути является «наследованием реализации».

Продолжение следует…

Источник:
https://blog.adamfurmanek.pl/2021/02/06/types-and-programming-languages-part-4/
День восемьсот восемьдесят девятый. #TypesAndLanguages
4. Проблема Алмаза. Продолжение
Начало

Википедия определяет проблему алмаза как ситуацию, когда два класса B и C наследуются от класса A, переопределяют что-то, а затем класс D наследуется от классов B и C, не переопределяя член из A. Когда мы теперь хотим использовать член из A в классе D, мы не знаем, какой из них использовать (из B или из C).

Важно понимать, что это не имеет ничего общего с технической реализацией. Это логическая проблема (какой из членов выбрать), а не техническая.

Рассмотрим наследование сигнатуры:
interface A {
void foo();
}
interface B {
void foo();
}
class C : A, B {
public void foo(){
Console.WriteLine("FOO");
}
}

Здесь никаких проблем нет, поскольку сигнатуры одинаковые, и неважно, какую из них использовать. Но что, если мы изменим тип результата:
interface A {
object foo();
}
interface B {
string foo();
}
class C : A, B {
public string foo() {
return "Foo";
}
}

В Java это сработает, в C# компилятор выдаст ошибку, требуя реализации
A.foo() из-за другого типа результата. Здесь «Ситуация Алмаза» решается в Java, но является проблемой в C#.

Что же с реализацией интерфейса по умолчанию?
interface A {
void foo() => Console.WriteLine("A");
}
interface B {
void foo() => Console.WriteLine("B");
}
class C : A, B { }

Следующий код
C c = new C();
c.foo();

Вызовет ошибку компиляции в обоих языках. В Java с сообщением, что интерфейсы A и B несовместимы. В C#, что класс C не содержит реализации метода foo. Всё дело в том, что C# заставляет использовать явную реализацию, и выбор остаётся за клиентским кодом (нужно привести экземпляр класса к одному из интерфейсов):
C c = new C();
((A)c).foo();

Окончание следует…

Источник:
https://blog.adamfurmanek.pl/2021/02/06/types-and-programming-languages-part-4/
День восемьсот девяностый. #TypesAndLanguages
4. Проблема Алмаза. Окончание
Начало
Продолжение

Так в чём же состоит Проблема Алмаза? Не в наследовании несовместимого, а в выборе, какой унаследованный член использовать. Ситуация усложняется, когда мы вводим состояние в базовый класс. Нам нужно решить, хотим ли мы иметь независимые состояния для каждого подкласса (обычное наследование) или разделять его между подклассами (виртуальное наследование). Если мы делимся состоянием, оно может легко сломаться (поскольку две разные реализации используют одни и те же переменные). Если нет, нам нужно указать, какие переменные мы имеем в виду в самом низком подклассе.

В C# и Java проблема решается ошибкой времени компиляции. Но в других языках есть другие решения, например в Scala приоритет получает член «крайнего правого» унаследованного класса.

Проблема Алмаза в Java и C# с первого дня
По сути Проблема Алмаза была в Java и C# с самого начала. Как мы уже говорили, проблема состоит в том, чтобы решить, какому из членов отдать приоритет. Например:
class A {
public string foo(long l) => "Long";
public string foo(double d) => "Double";
}

Что выведет следующий код?
A a = new A();
Console.WriteLine(a.foo(123));

У нас есть два метода с разными сигнатурами. Мы хотим вызвать метод и передать недопустимое значение - значение другого типа. Однако Java и C# достаточно «умны», и просто приводят значение к типу, который им больше нравится (в данном случае - long).

По сути, это та же Проблема Алмаза, что и раньше. У нас есть две вещи, и мы не можем решить, какую из них использовать. При реализациях интерфейса по умолчанию языки выдают ошибку компиляции, но при перегрузке метода они просто выбирают один метод вместо другого.

Это, кстати, может привести к нарушению совместимости. Представьте, что кто-то добавит ещё один метод foo(int i). Что будет с вашим кодом? Раньше int приводился к long, но после добавления нового метода приведение не требуется, и будет вызван новый метод. Это нарушает совместимость.

Итого
Хотя правильно сказать, что в Java и C# нет множественного наследования, лучше иметь в виду, что существует много уровней наследования, и мы должны быть конкретными. Мы можем унаследовать реализацию начиная с C# 8 — это множественное наследование или нет?

Хотя правильно сказать, что до 8 версии языка не было Проблемы Алмаза, по сути, проблема существует в перегрузке методов. И это имеет те же последствия.

Стоит помнить, как, казалось бы, совершенно разные языковые элементы приводят к аналогичным проблемам. Мы все «боимся» Проблемы Алмаза, но не боимся перегрузки методов. Более того, считаем что это классная функция, пока однажды не нарушим совместимость.

Источник: https://blog.adamfurmanek.pl/2021/02/06/types-and-programming-languages-part-4/
День восемьсот девяносто первый. #DesignPatterns #Microservices
Паттерны в Микросервисах
3. Разделение Ответственности Запросов и Команд (CQRS)
При использовании паттерна Источников Событий иногда чтение данных из хранилища событий становится затруднительным. Чтобы получить сущность из хранилища, нам нужно обработать все события сущности. Кроме того, иногда у нас разные требования к согласованности и пропускной способности для операций чтения и записи.

В таких случаях мы можем использовать шаблон CQRS. В шаблоне CQRS функционал изменения данных системы (Команды) отделён от функционала чтения данных (Запросов). Шаблон CQRS имеет две формы: простую и расширенную, что приводит к некоторой путанице среди разработчиков.

В своей простой форме для чтения и записи используется единое хранилище данных, но отдельные сущности или модели ORM, как показано в левой части рисунка ниже. Это помогает обеспечить соблюдение принципов единственной ответственности и разделения ответственности, что приводит к более чистому дизайну.

В расширенной форме (см. в правой части рисунка ниже) для операций чтения и записи используются разные хранилища данных. Расширенный CQRS используется с паттерном Источников Событий. В зависимости от реализации могут использоваться разные типы хранилищ для записи и для чтения. Хранилище для записи - это краеугольный камень всей системы.

Для приложений с интенсивным чтением или микросервисной архитектуры в качестве хранилища для записи используется база данных OLTP (любая база данных SQL или NoSQL, предлагающая гарантию транзакции ACID) или платформа распределённого обмена сообщениями. В приложениях с интенсивной записью (для высокой масштабируемости и пропускной способности записи) используется база данных с возможностью горизонтального масштабирования для записи (глобальные облачные базы данных). В хранилище для записи сохраняются нормализованные данные.

В качестве хранилища для чтения используется база данных NoSQL, оптимизированная для поиска (например, Apache Solr, Elasticsearch) или чтения (хранилище пар ключ-значение, хранилище документов). Во многих случаях, когда требуется SQL-запрос, используются масштабируемые для чтения базы данных SQL. В хранилище для чтения сохраняются денормализованные и оптимизированные данные.

Данные копируются из хранилища для записи в хранилище для чтения асинхронно. В результате хранилище чтения отстает от хранилища записи и имеет место Окончательная Согласованность (Eventual Consistency).

Плюсы
- Более быстрое чтение данных в микросервисах, управляемых событиями.
- Высокая доступность данных.
- Системы чтения и записи могут масштабироваться независимо.

Минусы
- Хранилище для чтения слабо согласовано (окончательная согласованность)
- Общая сложность системы увеличивается. Чрезмерная увлечённость CQRS может поставить под угрозу весь проект.

Когда использовать
- В высокомасштабируемой микросервисной архитектуре, где используется паттерн источников событий.
- В сложной модели предметной области, где для чтения данных требуется запрос в несколько хранилищ данных.
- В системах, где операции чтения и записи имеют разную нагрузку.

Когда не использовать
- В микросервисной архитектуре, где объём событий незначителен, создание моментального снимка хранилища событий для вычисления состояния объекта является лучшим выбором.
- В системах, где операции чтения и записи имеют одинаковую нагрузку.

Поддержка
Хранилища для Записи:
EventStoreDB, Apache Kafka, Confluent Cloud, AWS Kinesis, Azure Event Hub, GCP Pub/Sub, Azure Cosmos DB, MongoDB, Cassandra, Amazon DynamoDB
Хранилища для Чтения: Elastic Search, Solr, Cloud Spanner, Amazon Aurora, Azure Cosmos DB, Neo4j
Фреймворки: Lagom, Akka, Spring, akkatecture, Axon, Eventuate

Подробнее:
- CQRS
- Что такое шаблон CQRS?

Источник: https://towardsdatascience.com/microservice-architecture-and-its-10-most-important-design-patterns-824952d7fa41
👍1
День восемьсот девяносто второй. #ЧтоНовенького
GitHub и OpenAI Запускают Новую Утилиту, Которая Генерирует Код за Вас
GitHub и OpenAI запустили предварительную техническую версию нового инструмента с искусственным интеллектом под названием Copilot, который находится внутри редактора кода Visual Studio и автоматически генерирует фрагменты кода.

Согласно GitHub, Copilot делает больше, чем просто выдаёт код, который где-то видел раньше. Вместо этого он анализирует код, который вы уже написали, и генерирует новый соответствующий код, включая определённые функции, которые ранее использовались. На веб-сайте проекта выложено несколько примеров популярных блоков кода, которые программистам приходится писать раз за разом, вроде получения ответа от API, нахождения количества дней между датами, и даже написания юнит-тестов.

Пока Copilot работает на Python, JavaScript, TypeScript, Ruby и Go.

GitHub рассматривает это как эволюцию парного программирования, когда два программиста работают над одним и тем же проектом, чтобы выявлять ошибки друг друга и ускорять процесс разработки. В Copilot один из этих программистов является виртуальным.

Этот проект - первый крупный результат инвестиций Microsoft в OpenAI, исследовательскую фирму, которую возглавляет Сэм Альтман. С тех пор как Альтман взял бразды правления, OpenAI перешёл от некоммерческого статуса к модели «с ограничением прибыли», принял инвестиции от Microsoft и начал лицензировать свой алгоритм генерации текста GPT-3.

GPT-3 - это флагманский алгоритм генерации текстов от OpenAI, который может генерировать текст, иногда неотличимый от человеческого языка. Он это делает так убедительно из-за огромного массива в 175 миллиардов параметров настройки, которые позволяют алгоритму связывать отношения между буквами, словами, фразами и предложениями.

Copilot построен на новом алгоритме под названием OpenAI Codex, который технический директор OpenAI Грег Брокман описывает как потомка GPT-3. GPT-3 генерирует английский текст, а Codex генерирует код. OpenAI планирует выпустить версию Codex в своём API этим летом, чтобы разработчики могли создавать свои собственные приложения с использованием этой технологии.

Codex был обучен на терабайтах открыто кода, взятого с GitHub, а также на примерах на английском языке. Разработчики говорят, что предложенные варианты кода были верны в 43% случаев с первого раза и в 57% случаев, когда алгоритму давалось до 10 попыток. При этом алгоритм будет становиться умнее со временем.

Хотя первые пользователи на сайте восторженно отзываются о росте продуктивности, который обеспечивает Copilot, GitHub подразумевает, что не весь используемый код был проверен на наличие ошибок, небезопасных действий или защиты личных данных. Компания пишет, что они установили несколько фильтров, чтобы Copilot не генерировал оскорбительные выражения, но фильтры могут быть не идеальны.

Компания также предупреждает, что модель может предлагать адреса электронной почты, ключи API или номера телефонов, но это происходит редко, и эти данные были скорее всего синтетические или псевдослучайно сгенерированные алгоритмом. При этом код, созданный Copilot, во многом оригинален. Тест, проведенный GitHub, показал, что только 0,1% сгенерированного кода можно было точно найти в обучающем наборе.

Прямо сейчас Copilot находится в ограниченной предварительной технической версии, но вы можете зарегистрироваться на веб-сайте проекта, чтобы получить к нему доступ.

Источник: https://www.theverge.com/2021/6/29/22555777/github-openai-ai-tool-autocomplete-code
День восемьсот девяносто третий. #Оффтоп #КакСтатьСеньором
Когда Дела Идут не по Плану
Когда что-то идёт не так, а такое рано или поздно обязательно случится, золотое правило - свести к минимуму влияние на клиента.

Раньше, когда что-то шло не так, моей первой реакцией было устранить проблему. Оказывается, это не самое оптимальное решение. Вместо того, чтобы исправлять то, что пошло не так, даже если это «изменение одной строки», первое, что нужно сделать, - это откатиться. Вернитесь в предыдущее рабочее состояние. Это самый быстрый способ вернуть клиентов к рабочей версии.

Только после этого можно искать, что пошло не так, и исправлять эти ошибки. Тот же подход, что и с глючащей машиной в вашем кластере: отключите её, отметьте как недоступную, прежде чем пытаться выяснить, что с ней не так.

Странно, как мои естественные склонности и инстинкты расходятся с оптимальным решением. Думаю, что этот инстинкт также ведёт меня по более долгому пути в исправлении ошибок. Иногда мне кажется, что это не работает, потому что что-то не так с тем, что я написал, и я погружаюсь в исходники, просматривая каждую строку кода. Что-то вроде «поиска в глубину».

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

Теперь мой подход заключается в том, чтобы выполнять «поиск в ширину» перед «поиском в глубину», чтобы избавиться от узлов верхнего уровня. Что я могу проверить на верхнем уровне?
- Машина работает?
- Установлен правильный код?
- Конфигурация на месте?
- Правильно ли задана конфигурация для конкретного кода, например, правильная ли маршрутизация в коде?
- Обновлена ли база данных?
И только потом можно искать ошибку в коде.

«Мы думали, что nginx некорректно установился на машине, но оказалось, что просто в конфигурации было установлено значение false.»

Конечно, не нужно делать так всегда. Иногда достаточно просто сообщения об ошибке, чтобы сузить пространство поиска и сразу лезть в код.

Когда я не могу понять, в чём проблема, я стараюсь свести к минимуму изменения кода, чтобы выяснить, что не так. Чем меньше изменений, тем быстрее я смогу найти реальную проблему.

Я также теперь веду дневник ошибок, на устранение которых мне потребовалось более 1 часа: Чего мне не хватило? О чём я не подумал? Обычно это какое-нибудь мелкое дерьмо, которое я забыл проверить, например, настройка маршрутизации, соответствие версии схемы и версии сервиса и т. д. Это ещё один шаг знакомства с используемым мной технологическим стеком, и то, что даёт только опыт: интуицию для точного понимания того, почему что-то не работает.

Источник: https://neilkakkar.com/things-I-learnt-from-a-senior-dev.html
Автор оригинала – Neil Kakkar
День восемьсот девяносто четвёртый. #Оффтоп
CUPID
Сегодня посоветую вам очередное видео с просторов Ютуба. В этом видео Дэниел Норт посягает на самое святое. Оказывается SOLID уже не торт!

С некоторым проектами работается легче, чем с другими. Это верно для приложений, сервисов, библиотек, фреймворков и даже самих языков программирования. Это сугубо личный выбор или есть универсальные характеристики программного обеспечения, которые могут сделать работу с кодом приятной?

Дэниел думал об этом очень давно, особенно с тех пор, как несколько лет назад он для развлечения «наехал» на принципы SOLID, и получил в ответ порцию хейта. Его пост о том, почему он считает, что SOLID устарел, оказался на первой странице Hacker News!

Впоследствии он систематизировал свои мысли в виде собственного лаконичного пятибуквенного акронима CUPID. Это не принципы проектирования, а скорее свойства хорошей кодовой базы. То есть их нельзя нарушить, но можно в большей или меньшей степени им соответствовать. Итак, CUPID – означает, что кодовая база обладает следующими свойствами.

1. Composable (Интегрируемый)
Это значит, что модуль или библиотека легко встраивается в проект и может быть использована повторно. Код, который легко использовать повторно, используется повторно снова, и снова, и снова. Некоторые характеристики такого кода:
- Компактный интерфейс – требует меньше изучения, меньше возможностей использовать его неправильно, меньше конфликтов.
- Имя и описание чётко выражают намерение – такую библиотеку или NuGet-пакет легче найти и легче понять, что она делает.
- Имеет минимальное количество зависимостей.

2. Unix философия
Код делает одну вещь, но делает её хорошо. Примером послужили команды консоли в Unix. Например, ls, которая всего лишь выводит список файлов, но имеет огромное количество параметров и способов сортировки и обработки получаемого результата.
Кроме того, Unix философия подразумевает, что результат одной программы будет входными данными для другой и поощряет пайплайны.

3. Predictable (Предсказуемый)
Код делает то, что вы от него ожидаете. Всё равно, что «проходит все тесты», как говорят в TDD, но более обобщённо. Это также относится, например, к структуре проекта, где все вещи названы так и расположены там, где вы этого ожидаете, поэтому даже не знакомый с кодовой базой человек может быстро найти нужный кусок кода.

4. Idiomatic (Идиоматический)
Код легко понимается и использует характерные для языка или проекта конструкции, ключевые слова или выражения, стандарты кодирования и т.п. согласованно во всей кодовой базе.

5. Domain-based (Предметно-ориентированный)
Это означает не только использование в коде характерных для предметной области терминов, но и построение структуры проекта в соответствии с доменом, а не фреймворком.

Таким образом, по задумке Дэниела, чем больше код соответствует каждому из этих свойств, тем приятнее с ним работать программисту.

Подробнее Дэниел рассказывает о CUPID в подкасте DotNet Rocks.

Что скажете?
👍2
День восемьсот девяносто пятый. #DesignPatterns #Microservices
Паттерны в Микросервисах
4. Saga
Если вы используете микросервисную архитектуру с базой данных на микросервис, то управление согласованностью с помощью распределённых транзакций является сложной задачей. Вы не можете использовать традиционный протокол двухфазной фиксации (2PC), поскольку он либо не масштабируется (базы данных SQL), либо не поддерживается (многие базы данных NoSQL).

Вы можете использовать паттерн Saga для распределённых транзакций в микросервисной архитектуре. Saga - это старый паттерн, разработанный в 1987 году как концептуальная альтернатива длительным транзакциям в базах данных SQL. Но современный вариант этого паттерна отлично работает и для распределённой транзакции. Паттерн Saga - это последовательность локальных транзакций, в которой каждая транзакция обновляет данные в хранилище данных в рамках одного микросервиса и публикует событие или сообщение. Первая транзакция в Saga инициируется внешним запросом (событием или действием). После завершения локальной транзакции (данные сохраняются в хранилище данных, а сообщение или событие публикуются) опубликованное сообщение/событие запускает следующую локальную транзакцию в Saga (см. картинку ниже).

Если локальная транзакция терпит неудачу, Saga выполняет серию компенсирующих транзакций, которые откатывают изменения предыдущих локальных транзакций.

В основном существует два варианта координации транзакций в Saga:
- Хореография: децентрализованная координация, при которой каждый микросервис производит и прослушивает события/сообщения других микросервисов и решает, следует ли предпринять действие или нет.
- Оркестрация: централизованная координация, при которой оркестратор сообщает участвующим микросервисам, какую локальную транзакцию необходимо выполнить.

Плюсы
- Обеспечение согласованности посредством транзакций в высокомасштабируемой или слабо связанной, управляемой событиями микросервисной архитектуре.
- Обеспечение согласованности посредством транзакций в микросервисной архитектуре, где используются базы данных NoSQL без поддержки 2PC.

Минусы
- Необходимо обрабатывать временные отказы и обеспечивать идемпотентность.
- Трудно отлаживать, и сложность растёт по мере увеличения количества микросервисов.

Когда использовать
- В высокомасштабируемой, слабо связанной микросервисной архитектуре, где используются источники событий https://t.iss.one/NetDeveloperDiary/1084.
- В системах, где используются распределённые базы данных NoSQL.

Когда не использовать
- Слабо масштабируемые транзакционные системы с базами данных SQL.
- В системах, где существует циклическая зависимость между сервисами.

Поддержка
Axon, Eventuate, Narayana

Подробнее
- Saga — распределённые транзакции
- Microservices Pattern: Saga

Источник: https://towardsdatascience.com/microservice-architecture-and-its-10-most-important-design-patterns-824952d7fa41
👍2
День восемьсот девяносто шестой. #BestPractices
Лучшие Практики Использования RESTful API. Начало
Сегодня рассмотрим лучшие практики, которые можно использовать при работе с RESTful API.

1. Используйте JSON
В наши дни JSON является общепринятым стандартом для взаимодействия с API. Это предпочтительный выбор по сравнению с XML, поскольку он значительно снижает полезную нагрузку, что приводит к более быстрой передаче данных. Следовательно, ваши REST API должны использовать JSON для связи между клиентом и сервером. Обратите внимание, что REST не зависит от формата, что означает, что вы можете использовать любой формат для обмена данными, например XML, HTML, JSON и даже пользовательские форматы. Чтобы гарантировать, что ваш REST API обменивается данными в JSON, вы можете задать заголовок ответа Content-Type: application/json.

2. Версионирование
Версионирование - это процесс управления изменениями в API. Рекомендуется всегда добавлять версии в свой API. При разработке REST API следует помнить, что изменения неизбежны. Возможно, вам придётся внести критические изменения, чтобы удовлетворить потребности клиентов.
Такое критическое изменение может потребоваться из-за изменения формата данных ответа, изменения типа ответа, введения новых функций, редактирования или удаления существующих функций и т. д. Следовательно, крайне важно, чтобы у вас была стратегия реализации изменений.
Версионирование - одна из таких стратегий. Существуют разные варианты реализации версионирования API:
- URI
Это простейшая стратегия передачи информации о версии: поместить номер версии в путь URI. При этом по соглашению номер версии начинается с префикса «v»:
https://www.mywebsite.com/api/v1.0/products
https://www.mywebsite.com/api/v2.1/orders

- Параметр запроса
Вы можете указать информацию о версии в параметре запроса. При этом базовый URL останется прежним:
https://www.mywebsite.com/api/products/{id}?version=1.0
Это более гибкий подход к запросу версии ресурса.

- Заголовок запроса
Ещё один вариант - использовать заголовок Accept:
Accept: version=1.0
В этом случае вам не нужно вносить никаких изменений в URL-адрес.

3. Документация
API неполон без надлежащей документации. Документация должна быть общедоступной и содержать сведения о конечных точках API и циклах запроса/ответа. Помните, что после того, как ваш API станет общедоступным, вы не должны вносить изменений без уведомления.

4. Всегда используйте SSL
Всегда используйте SSL. Здесь вообще не может быть никаких исключений. К вашему API можно получить доступ практически из любого места, где есть подключение к Интернету. Следовательно, вы должны убедиться, что ваши API безопасны. Связь между сервером и клиентом, использующим ваш API, должна быть защищённой и зашифрованной.

Окончание следует…

Источник:
https://www.developer.com/web-services/best-practices-restful-api/
День восемьсот девяносто седьмой. #BestPractices
Лучшие Практики Использования RESTful API. Окончание
Начало

5. Аккуратно обрабатывайте ошибки
Вы должны аккуратно обрабатывать ошибки и возвращать соответствующие коды ответа HTTP, когда ошибка произошла. Коды ответа HTTP предоставят достаточно информации, чтобы понять причину ошибки и возникшую проблему.
Вот список распространённых кодов ответа HTTP:
- 400 Bad Request – неправильный запрос со стороны клиента
- 401 Unauthorized – неавторизованный клиент пытается получить доступ к ресурсу
- 403 Forbidden – клиент аутентифицирован, но не имеет прав для доступа к определённому ресурсу
- 404 Not Found – запрошенный ресурс не найден
- 415 Unsupported Media Type – клиент отправляет данные в формате, который не поддерживается сервером или для данного URI
- 422 Unprocessable Entity – клиент отправляет данные в понятном серверу формате, но сервер не может их обработать (например, отправлен неверный тип объекта)
- 500 Internal server error – сервер столкнулся с непредвиденными обстоятельствами, мешающими ему исполнить запрос
- 502 Bad Gateway – веб-сервер, к которому вы подключились, действует как прокси-сервер для ретрансляции информации с другого сервера, но получил неверный ответ от этого другого сервера
- 503 Service Unavailable – что-то неожиданное случилось на стороне сервера, и API недоступен.

Полезно возвращать подробные сведения об ошибке в теле ответа. Это поможет разработчикам выполнить отладку и обнаружить причину проблемы. Например:
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"error": "Неверный запрос.",
"detail": {
"address": "Поле address является обязательным."
}
}

Следует последовательно использовать коды состояния. Например, если вы использовали 200 OK для конечной точки GET, вы должны использовать один и тот же код состояния для всех конечных точек GET. Вот примеры кодов ответа на разные типы запросов для обеспечения единообразия:
GET: 200 OK
POST: 201 Created
PUT: 200 OK
PATCH: 200 OK
DELETE: 204 No Content

6. Избегайте использования глаголов в URI
Вы не должны описывать выполняемое действие с помощью глаголов в URL-адресе. Использование глаголов в REST API - плохая практика. Сам метод HTTP - это глагол: GET, PUT, POST, DELETE, - а URL-адрес всегда должен содержать существительные. Из этого правила есть несколько исключений. Например, глаголы используются для некоторых конкретных действий:
/login
/logout
/register

7. Повышение производительности
- Про использование формата JSON вместо XML мы уже говорили выше.
- Используйте вызовы PATCH, когда объём данных, подлежащих обновлению, небольшой. В запросе PATCH обычно передаётся особый тип данных, говорящий серверу, какие поля объекта следует изменить, и как.
- Ограничьте количество полей, возвращаемых API. В большинстве случаев не требуется возвращать полное представление ресурса. Уменьшая количество полей, вы сможете минимизировать сетевой трафик и повысить производительность API.

Итого
Не существует какого-либо конкретного подхода к разработке API - вам просто нужно придерживаться лучших практик и рекомендаций. REST API должны быть полными, краткими, удобными для работы и хорошо документированными. Потребители ваших API должны иметь возможность легко использовать их и работать с ними.

Источник: https://www.developer.com/web-services/best-practices-restful-api/
👍1
День восемьсот девяносто восьмой. #DesignPatterns #Microservices
Паттерны в Микросервисах
5. Бэкенды для Фронтендов (BFF)
В современной разработке бизнес-приложений, особенно в микросервисной архитектуре, фронтенд и серверные приложения отделены друг от друга и представляют собой отдельные сервисы. Они общаются через API или GraphQL. Если в приложении также есть клиент для мобильного, использование одного и того же бэкенд микросервиса как для веб-клиента, так и для мобильного клиента становится проблематичным. Требования к API мобильного клиента обычно отличаются от требований веб-клиента, поскольку у них другой размер экрана, способ отображения, производительность, источник энергии и пропускная способность сети. См. картинку ниже.

Паттерн Бэкенды для Фронтендов можно использовать в сценариях, где каждый тип клиента получает отдельный серверный модуль, настроенный для конкретного пользовательского интерфейса. Паттерн также обеспечивает другие преимущества, например, выступает в качестве фасада для подчинённых микросервисов, тем самым уменьшая трафик между пользовательским интерфейсом и нижестоящими микросервисами. Кроме того, в сценарии с высокой степенью защиты, когда нижестоящие микросервисы развертываются в сети DMZ (демилитаризированной зоны), BFF используется для обеспечения более высокой безопасности.

Плюсы
- Разделение проблем между сервисами. Возможность оптимизировать их для конкретного пользовательского интерфейса.
- Обеспечение более высокого уровня безопасности.
- Снижение трафика между пользовательским интерфейсом и микросервисами нижестоящего уровня.

Минусы
- Дублирование кода между сервисами.
- Размножение сервисов в случае использования множества разных пользовательских интерфейсов (например, Smart TV, Web, Mobile, Desktop).
- Требуется тщательная разработка и реализация, поскольку BFF не должны содержать никакой бизнес-логики, только логику и поведение, специфичные для клиента.

Когда использовать
- Если приложение имеет несколько пользовательских интерфейсов с разными требованиями к API.
- Если требуется дополнительный уровень между пользовательским интерфейсом и микросервисами нижестоящего уровня по соображениям безопасности.
- Если при разработке пользовательского интерфейса используются микро-интерфейсы.

Когда не использовать
- Если у приложения несколько пользовательских интерфейсов, но они используют один и тот же API.
- Если основные микросервисы не развернуты в DMZ.

Поддержка
Большинство фреймворков для бэкенда (Node.js, Spring, Django, Laravel, Flask, Play, и т.д.).

Подробнее
- Бэкенды для фронтендов
- Microservices Pattern: API Gateway / Backends for Frontends

Источник: https://towardsdatascience.com/microservice-architecture-and-its-10-most-important-design-patterns-824952d7fa41
👍3
День восемьсот девяносто девятый. #ЧтоНовенького
10 Советов по Повышению Продуктивности в Visual Studio 2019. Начало

В серии #ЧтоНовенького я много писал про новые функции в Visual Studio, но обычно это было про функции, которые только-только выходили в превью версиях. Поэтому сегодня небольшое саммари про инструменты, которые вы уже можете использовать в стабильной версии (16.10.3).

1. Умный перевод строки
Сочетание клавиш Shift+Enter после заголовка класса или метода, либо после названия блока (if, for, и т.п.) приведёт к тому, что Visual Studio автоматически поставит и выровняет фигурные скобки для блока, а также поместит курсор в начало пустой строки между скобками. Очень удобно.

2. Добавление недостающих директив using
Если вам нужно скопировать код и вставлять его в другой файл, иногда приходится тратить время на добавление директив using, которых нет в целевом файле. Чтобы не делать этого, зайдите в Options > Text Editor > C# > Advanced (Настройки > Текстовый редактор > C# > Дополнительно). Там в блоке Using Directives (Директивы Using) отметьте флажок «Add missing using directives on paste» (Добавлять недостающие директивы using при вставке).

3. Значок иерархии наследования
Если вы останетесь в том же окне настроек из пункта 2, и прокрутите до конца вниз, вы обнаружите ещё одну интересную функцию «Show inheritance margin» (Показывать поле наследования). Придётся перезагрузить студию, но это крутая штука. Для иерархий классов (а также для реализаций интерфейсов) слева от заголовков классов и методов будет показываться значок со стрелкой вверх или вниз, позволяющий быстро перейти к родительскому (стрелка вверх) или дочернему (стрелка вниз) классу или интерфейсу. Для родителей понятно, что можно найти имя класса в заголовке, а вот быстро вывести список всех наследников может быть очень полезно.

4. Убрать неиспользуемые ссылки
Это можно сделать из контекстного меню проекта «Remove Unused References» (Удалить Неиспользуемые Ссылки). Откроется диалоговое окно, где вы сможете просмотреть все ссылки на проекты, библиотеки или NuGet пакеты, которые не используются, и отметить те, которые вы хотите сохранить или удалить.

5. Графический интерфейс для .editorconfig
Файл .editorconfig позволяет вам обеспечить одинаковый стиль кодирования в ваших проектах. Особенно это полезно для командных проектов, где могут быть разные стандарты. Но раньше настраивать его было откровенно больно, т.к. это был просто текстовый файл. Теперь для него разработан GUI. В контекстном меню проекта или решения выберите Add > New EditorConfig (либо Add > New item… > editorconfig File). Откроется окно с сотнями настроек, которые можно задать через удобный графический интерфейс.

6. Очистка кода
Применить заданные в пункте 5 настройки в одно нажатие довольно легко с помощью функции Code Cleanup. Это маленький значок метлы в сроке состояния документа. Либо просто Ctrl+K, Ctrl+E. Параметры очистки кода также можно настроить.

Окончание следует…

Источник:
https://www.youtube.com/watch?v=b4T63cdGs0U
День девятисотый. #ЧтоНовенького
10 Советов по Повышению Продуктивности в Visual Studio 2019. Окончание
Начало

7. Добавление параметров в конструктор
Если встать курсором на определение свойства или поля класса, то меню быстрых действий (Ctrl+. или Alt+Enter) позволяет добавлять свойства класса в параметры конструктора (пункт меню «Add parameters to <имя конструктора>»). Кроме того, возможно добавить необязательный параметр, а если конструкторов несколько, будет предложено выбрать из них.
И наоборот, если встать курсором на параметр конструктора, в меню быстрых действий будут пункты «Create and assign …», позволяющие добавить и задать:
- свойство или поле с именем параметра, на котором стоит курсор,
- все недостающие свойства или поля из параметров конструктора.

8. Проверка параметров на null
Если встать курсором на параметр конструктора или метода, меню быстрых действий также может предложить добавить проверку на null («Add null check») для текущего параметра или для всех параметров ссылочных типов. При этом для строк есть возможность проверки на IsNullOrEmpty или IsNullOrWhiteSpace. По умолчанию для null генерируется выброс ArgumentException.

9. Конвертация в полное свойство
По умолчанию при генерации свойств используется сокращённый вариант автосвойства, например:
public string Name { get; set; }
Меню быстрых действий предлагает возможность преобразовать свойство в полный вариант с приватным полем (пункт меню «Convert to full property»), что выдаст примерно следующий код:
private string _name;
public string Name { get => _name;
set => _name = value; }
Можно пойти дальше, встать на get или set и выбрать из быстрых действий пункт «Use block body for accessors», что преобразует соответствующий аксессор в блочный вариант, например:
public string Name { get => _name; 
set
{
_name = value;
}
}
Аналогично возможно и обратное преобразование с помощью пункта меню «Use auto property».

10. Новый редактор Razor
В настройках среды Options > Environment > Preview Features (Настройки > Среда > Предварительный просмотр отметьте флажок «Enable experimental Razor editor» (Включить экспериментальный редактор Razor).
В новом редакторе Razor есть улучшенный механизм форматирования, который более консервативен, чем старый, а также намного умнее в том, как он обрабатывает ваш код. Некоторые быстрые действия теперь также доступны в файлах Razor:
- добавление директивы @using или полного имени типа,
- добавление проверки на null,
- переименование компонентов Blazor из тегов (файл компонента будет изменён автоматически),
- создание компонента Blazor из неизвестного тега,
- извлечение блока @code в файл отделённого кода,
- переход к определению компонента Blazor по F12.

Источник: https://www.youtube.com/watch?v=b4T63cdGs0U
День девятьсот первый. #Оффтоп #97Вещей
97 Вещей, Которые Должен Знать Каждый Программист
91. Подробный Логгинг Нарушит Ваш Сон
Когда я встречаю систему, которая уже какое-то время находится в стадии разработки или производства, первым признаком реальных проблем всегда является грязный журнал. Вы знаете, о чем я говорю: когда в обычном режиме работы нажатие на одну ссылку на веб-странице приводит к шквалу сообщений в единственном журнале, который предоставляет система. Слишком подробный логгинг может быть столь же бесполезным, как и полное его отсутствие.

Если ваши системы похожи на мои, то, когда ваша работа сделана, чья-то чужая работа только начинается. Надеемся, что после того, как система будет разработана, она проживет долгую и благополучную жизнь, обслуживая клиентов (если вам повезёт). Как вам узнать, что что-то идёт не так, когда система находится в эксплуатации, и что в этом случае делать?

Может быть, кто-то следит за вашей системой за вас, а может вы будете следить за ней сами. В любом случае журналы, скорее всего, будут частью мониторинга. Если возникает проблема, и вас нужно разбудить среди ночи, чтобы решить её, вы должны быть уверены, что проблема действительно серьёзная. Если моя система умирает, я хочу знать об этом. Но если это просто мелкие косяки, я предпочитаю наслаждаться своим прекрасным сном.

Для многих систем первым признаком того, что что-то идёт не так, является запись сообщения в какой-либо журнал. Обычно это будет журнал ошибок. Так что сделайте себе одолжение: с первого дня разработки сделайте так, что, если уж что-то пишется в журнал ошибок, то вы готовы к тому, что кто-нибудь позвонит и разбудит вас по этому поводу посреди ночи. Если вы можете смоделировать нагрузку на свою систему во время тестирования, последующее отсутствие лишних записей в журнале ошибок является хорошим первым признаком того, что ваша система достаточно надёжна. В противном случае вы получите раннее предупреждение, что это не так.

Распределённые системы добавляют ещё один уровень сложности. Вы должны решить, что делать с ошибкой во внешней зависимости. Если ваша система сильно распределена, это может быть обычным явлением. Убедитесь, что ваша политика ведения журнала учитывает это.

В общем, лучший показатель того, что всё в порядке, — это то, что сообщения с более низким приоритетом успешно записываются. Мне нужно примерно одно сообщение в журнале уровня INFO для каждого значительного события приложения.

Заваленный сообщениями журнал свидетельствует о том, что систему будет трудно контролировать, когда она попадёт в производство. Если вы не ожидаете, что в журнал ошибок будет постоянно что-то записываться, вам будет намного проще понять, что делать, когда там действительно что-то появится.

Источник: https://www.oreilly.com/library/view/97-things-every/9780596809515/
Автор оригинала – Johannes Brodwall
День девятьсот второй. #DesignPatterns #Microservices
Паттерны в Микросервисах
6. API-шлюз (API Gateway)
В архитектуре микросервисов пользовательский интерфейс обычно соединяется с несколькими микросервисами. Если микросервисы являются слишком узкоспециализированными (FaaS), клиенту может потребоваться подключение к большому количеству микросервисов, что становится сложным и требует множества различных коммуникаций. Кроме того, сервисы, включая их API, могут развиваться. Также крупные системы обычно добавляют стандартные задачи (SSL-терминацию, аутентификацию, авторизацию, троттлинг, ведение журнала и т. д.).

Один из возможных способов решения этих проблем - использовать API-шлюз. Он располагается между клиентским приложением и внутренними микросервисами и действует как фасад. API-шлюз может работать как обратный прокси-сервер, перенаправляя клиентский запрос на соответствующий внутренний микросервис. Также может поддерживать разветвление клиентского запроса на несколько микросервисов, а затем возвращать агрегированные ответы клиенту. Кроме того, он может решать важные задачи общие для всех микросервисов. См. картинку ниже.

Достоинства
- Предлагает слабую связь между фронтендом и микросервисами на бэкенде.
- Уменьшает количество вызовов между клиентом и микросервисами.
- Повышает безопасность за счет централизованной SSL-терминации, аутентификации и авторизации.
- Централизованно управляет общими задачами, например, ведением журнала и мониторингом, троттлингом и балансировкой нагрузки.

Недостатки
- Сбой в API-шлюзе становится «единой точкой отказа» в микросервисной архитектуре.
- Увеличивает задержку из-за дополнительного сетевого вызова.
- Если не масштабировать API-шлюз, он легко может стать узким местом для всей системы.
- Увеличивает стоимость разработки и обслуживания.

Когда использовать
- В сложной микросервисной архитектуре это почти обязательно.
- В крупных системах API-шлюз является обязательным для централизации безопасности и решения общих задач.

Когда не использовать
- В частных проектах или небольших компаниях, где безопасность и централизованное управление не являются наивысшим приоритетом.
- Если количество микросервисов невелико.

Поддержка
Amazon API Gateway, Azure API Management, Ocelot, Apigee, Kong, WSO2 API Manager

Подробнее
- Использование API-шлюзов в микросервисах
- Microservices Pattern: API Gateway / Backends for Frontends

Источник: https://towardsdatascience.com/microservice-architecture-and-its-10-most-important-design-patterns-824952d7fa41
👍2