Как стать автором
Обновить
0
3
Дмитрий @Urgen

Умный, красивый, скромный

Отправить сообщение

Проблема с кодом выше в том, что при изменении имплементации DbClient без изменения его внешнего API тесты могут запросто поломаться

при изменении реализации DbClient сломается интеграционный тест. мок не сломается, т.к. его поведение задаётся в тесте

Я утверждаю, что вы ломаете контракт (в данном примере контракта вообще нет, но обычно он есть)

что вы подразумеваете под контрактом?

пример
public class MyService {
  
  private final DbClient dbClient;

  public void doSomething(Request request) {
    Long id = dbClient.getLastId();
    dbClient.update(id, request);
  }
}

public class DbClient {

  public Long getLastId() {
    //запрос в БД
  }

  public void update(Long id, Request request) {
    //запрос в БД
  }
}

public class MyServiceTest {

  private MyService myService;

  public void doSomethingTest() {
    DbClient dbClient = mock(DbClient.class);
    when(dbClient.getLastId()).thenReturn(1);
    doNothing().when(dbClient.update(any(), any()));

    Request request = createRequest();
    myService.doSomething();

    verify(dbClient).getLastId();
    veryfy(dbClient).update(eq(1), eq(request));
  }
}

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

  1. если такая операция вызывается только извне, создаётся обработчик для неё (handler/operation), последовательно вызывающий шаги (service)

  2. можно использовать движок бизнес-процессов, где "создание заявки на кредит" выделено в отдельный процесс, который можно переиспользовать. и вызываться он будет непосредственно движком БП.

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

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

для моков интерфейс не нужен. не скажу за другие ЯП, но в Java всё прекрасно мокается без использования интерфейсов

можно, но это не будет так хорошо масштабироваться по мере роста проекта

Нельзя пропустить обязательные поля или использовать поля другого типа.

враньё. я могу не заполнять поля в запросе. и это будет корректно с т.з. proto. а вот null выставить явно нельзя. только тут про это ни слова.

у меня на одной руке longines, чтобы смотреть дату-время (а ещё они красивые), а на другой браслет xiaomi с будильником и прочими уведомлениями. удобно.

у спецификации asyncApi есть особенность - там описываются параметры и запросов и ответов, а также топики/очереди, куда они кладутся (или откуда берутся). этим она принципиально отличается от openApi.

Ситуация:

  1. сервис ServiceA умеет отправлять сообщения формата FormatA и получать сообщения формата FormatB

  2. сервис ServiceB умеет отправлять сообщения формата FormatB и читать сообщения формата FormatA

вопросы:

  1. где должна лежать спецификация в этом случае, в сервисе ServiceA или в сервисе ServiceB?

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

Если бы контракт содержал только описание моделей, то можно было бы держать его в сервисе-отправителе.

Лайфхак - если непосредственно перед генерацией в контракте заменить asyncapi: 2.6.0 на openapi: 3.0.2, а модели предварительно вынести в блок

components:
  schemas:
    ...

для генерации моделей можно будет использовать gradle-плагин.

спросите легендарную hr хоум кредита, как мотивировать сотрудника. удивлён, что её не пригласили в качестве эксперта.

Отчасти с вами согласен, в массиве ошибок ошибки пишутся в произвольном формате, который схемой не задаётся. На нашем проекте о нём мы просто договорились. На бэке он генерится

Однако, если в email пришёл null, то это и есть null, т.к. в противном случае пришла бы ошибка.

про schema-first не понял

Минусом же можно отметить необходимость в реализации как схемы, так и кода — это может занимать чуть больше времени при разработке API + теперь появляется 2 источника, которые обязаны не конфликтовать друг с другом и быть полностью синхронизированы - лишнее звено, которое может поломаться.

Можно генерить код из схемы, тогда конфликтов не будет, а вам не придётся писать лишний код. Мы используем для генерации graphql-java-codegen. Он позволяет генерить как DTO, так и интерфейсы API, которые потом можно реализовать, используя ваш любимый фрейсворк.

  1. Убираем обязательность из полей ответа. Клиент сам может решить, что ему запрашивать.

  2. Пишем ошибку доступа в стандартный блок ошибок.

И не нужно заморачиваться с юнионами PersonItem/PersonError.

PostgREST даст вам возможность не только логику в БД положить, но и выставить наружу API! Вот только стоит ли так делать?

На Android в приложении Сбера работает

  1. Вопрос не понял

  2. Сейчас это не реализовано, но можно где-то в БД держать список полей, доступных роли. При запросе к API Gateway можно сравнить запрашиваемые поля с доступными и, в случае если запрошено что-то лишнее, вернуть ошибку

Такого нет. И чистый r2dbc это тоже не поддерживает. Это фишка Spring Data JPA, который может быть обёрткой для r2dbc. И я бы не сказал, что в Repository получается намного меньше кода. + для сложных запросов можно использовать query-dsl, который дружит с хибером.

Для Hibernate Reactive (а точнее для smallrye-mutiny, который в нём используется) есть возможность выполнить преобразования Uni/Multy <-> Mono/Flux. Так что если сейчас используется JPA/Hibernate, особых проблем быть не должно.

Есть смысл для начала выкинуть этот самый бут/mvc и переписать на той же яве.

Информация

В рейтинге
1 214-й
Откуда
Ижевск, Удмуртия, Россия
Дата рождения
Зарегистрирован
Активность