Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Structure and autoconfigurations #143

Merged
merged 16 commits into from
Sep 30, 2023
3 changes: 1 addition & 2 deletions .dx/group_vars/all.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ prefs: turing
lib_cid: "{{ lookup('ansible.builtin.pipe', 'git write-tree --prefix=libs')[:7] }}"
lib_cids:
signatures: "{{ lookup('ansible.builtin.pipe', 'git write-tree --prefix=libs/signatures')[:7] }}"
construction: "{{ lookup('ansible.builtin.pipe', 'git write-tree --prefix=libs/construction')[:7] }}"
storage: "{{ lookup('ansible.builtin.pipe', 'git write-tree --prefix=libs/storage')[:7] }}"
essentials: "{{ lookup('ansible.builtin.pipe', 'git write-tree --prefix=libs/essentials')[:7] }}"
messaging: "{{ lookup('ansible.builtin.pipe', 'git write-tree --prefix=libs/messaging')[:7] }}"
Expand Down Expand Up @@ -52,4 +51,4 @@ stack_images:
docker_entity: "{{ 'image' if image_repo == 'local' else 'manifest' }}"

app_deps:
foo: [essentials, construction, signatures, messaging, storage]
foo: [essentials, signatures, messaging, storage]
18 changes: 1 addition & 17 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,14 @@ updates:
schedule:
interval: "weekly"
- package-ecosystem: "maven"
directory: "/libs"
directory: "/"
open-pull-requests-limit: 10
schedule:
interval: "daily"
groups:
spring-framework:
patterns:
- "org.springframework*"
- package-ecosystem: "maven"
directory: "/apps"
schedule:
interval: "weekly"
ignore:
- dependency-name: "smecalculus.bezmen:*"
- package-ecosystem: "maven"
directory: "/tests"
schedule:
interval: "weekly"
ignore:
- dependency-name: "smecalculus.bezmen:*"
- package-ecosystem: "maven"
directory: "/tools"
schedule:
interval: "weekly"
- package-ecosystem: "docker"
directory: "/schemas/postgres"
schedule:
Expand Down
81 changes: 0 additions & 81 deletions README.adoc
Original file line number Diff line number Diff line change
@@ -1,84 +1,3 @@
= bezmen

Шаблон репозитория backend-проекта на Java.

== Ключевые принципы

Решение и конвейер как сущности первого класса::
Решение (solution) - система, реализуемая для клиентов. Конвейер (pipeline) - система, реализуемая для разработчиков.

Единый интерфейс для локальной разработки и непрерывной интеграции::
Семантика интерфейса подразумевает набор предопределенных абстракций, артефакты которых последовательно конструируются в ходе сборки. Перед началом конструирования выполняются обязательные проверки. Примеры абстракций: исходники (sources), бинарники (binaries), образы (images), стеки (stacks) и другие.

Контентная адресация (aka идентификация по содержимому)::
Контентная адресация (content addressability) делает конструирование *идемпотентным*. Для директорий с исходным кодом вычисляются их контентные идентификаторы (CID's). Затем вычисляются корневые идентификаторы решения и конвейера. Таким образом формируется 2 небольших дерева Меркла, хеши которых подставляются в качестве тегов/классификаторов артефактов соответствующих абстракций. Артефакт конструируется, только когда его контентный идентификатор меняется.

Множественность окружений, назначений и настроек::
Решение/конвейер используется клиентами/разработчиками в разных окружениях (environs) для разных назначений (usages) и с разными настройками (prefs). Ключевые окружения/назначения/настройки именуются и проверяются в рамках непрерывной интеграции. Устаревшие окружения/назначения/настройки снимаются с поддержки.

Тесты! Тесты! Тесты!::
Тестирование - один из самых важных (или даже самый важный) аспектов разработки! При тестировании проверяется поведение целевого объекта в различных ситуациях. В зависимости от подхода к подготовке зависимостей целевого объекта тесты можно разделить на 3 категории.
Модульные тесты (unit tests):::
Все зависимости целевого объекта глушатся (stub) или мокируются (mock).
Интеграционные тесты (integration tests):::
Все зависимости глушатся/мокируются кроме одной (иногда нескольких), которая честно конструируется.
Сквозные тесты (end-to-end tests):::
Все зависимости честно конструируются.

Роботизированная асинхронная заливка (merge)::
Позволяет избежать ошибок/конкуренции и, как следствие, неоправданных потерь на загруженных ветках (например, таких как main).

== Непрерывная интеграция (CI)

Планы делим на 2 категории:

. Проверяющие решение (solution)
. Проверяющие конвейер (pipeline)

=== Проверка решения

Решение проверяем во всех окружениях, в которых его могут эксплуатировать клиенты.

==== В пределах разумного (sanity)

Запускаем анализ зависимостей, линтинг, статический анализ, компиляцию, модульные тесты и собираем/публикуем бинарники.

==== Убедительная (convincing)

Все перечисленное ранее. Плюс запускаем интеграционные тесты, анализируем покрытие и собираем/публикуем образы.

==== За пределами сомнений (beyond doubt)

Все перечисленное ранее. Плюс запускаем сквозные тесты, собираем/публикуем решения.

=== Проверка конвейера

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

==== В пределах разумного (sanity)

Запускаем линтинг исходного кода конвейера.

==== За пределами сомнений (beyond doubt)

Запускаем весь конвейер на минимальном наборе тестов.

== Частые вопросы (FAQ)

[qanda]
Почему обязательными статусными проверками назначены тестовые отчеты, а не тестовые джобы?::
Это дает свободу выбора структуры джобов, т.к. в различных планах сборки она может быть разной.

== Консольный интерфейс (CLI)

ansible-playbook <abstraction>.yml [-l <selector>]

== Todo

. Релизная сборка
. Параллелизация модульных тестов
. Построение дерева хешей (взглянуть критически)
. Версионирование библиотек и приложений (взглянуть критически)
. Локальные проверки в удаленном репозитории
. Чистка артефактов в удаленном репозитории
. Поддержка нескольких версий Java
4 changes: 0 additions & 4 deletions apps/foo/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,6 @@
<groupId>${project.groupId}</groupId>
<artifactId>essentials</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>construction</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>messaging</artifactId>
Expand Down
69 changes: 26 additions & 43 deletions apps/foo/src/main/java/smecalculus/bezmen/construction/App.java
Original file line number Diff line number Diff line change
@@ -1,26 +1,18 @@
package smecalculus.bezmen.construction;

import static smecalculus.bezmen.configuration.OrmMode.MY_BATIS;
import static smecalculus.bezmen.configuration.OrmMode.SPRING_DATA;
import static smecalculus.bezmen.configuration.WebMode.SPRING_MVC;
import static smecalculus.bezmen.configuration.MessageMappingMode.SPRING_WEB_MVC;
import static smecalculus.bezmen.configuration.StateMappingMode.MY_BATIS;
import static smecalculus.bezmen.configuration.StateMappingMode.SPRING_DATA;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.jdbc.DataSourceHealthContributorAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.system.DiskSpaceHealthContributorAutoConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.aop.AopAutoConfiguration;
import org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration;
import org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration;
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration;
import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration;
import org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration;
import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration;
import org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.observation.web.servlet.WebMvcObservationAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementContextAutoConfiguration;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
Expand All @@ -32,7 +24,7 @@
import smecalculus.bezmen.messaging.SepulkaMsgMapper;
import smecalculus.bezmen.messaging.SepulkaMsgMapperImpl;
import smecalculus.bezmen.messaging.client.SepulkaClient;
import smecalculus.bezmen.messaging.springmvc.SepulkaController;
import smecalculus.bezmen.messaging.springwebmvc.SepulkaController;
import smecalculus.bezmen.storage.SepulkaDao;
import smecalculus.bezmen.storage.SepulkaDaoMyBatis;
import smecalculus.bezmen.storage.SepulkaDaoSpringData;
Expand All @@ -42,35 +34,26 @@
import smecalculus.bezmen.storage.springdata.SepulkaRepository;
import smecalculus.bezmen.validation.EdgeValidator;

@Import({ConfigBeans.class, ValidationBeans.class, MessagingBeans.class, StorageBeans.class})
@EnableAutoConfiguration(
exclude = {
LiquibaseAutoConfiguration.class,
AopAutoConfiguration.class,
DataSourceHealthContributorAutoConfiguration.class,
DiskSpaceHealthContributorAutoConfiguration.class,
EmbeddedWebServerFactoryCustomizerAutoConfiguration.class,
PersistenceExceptionTranslationAutoConfiguration.class,
SpringDataWebAutoConfiguration.class,
HealthContributorAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
LifecycleAutoConfiguration.class,
MultipartAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class,
RestTemplateAutoConfiguration.class,
TaskExecutionAutoConfiguration.class,
TaskSchedulingAutoConfiguration.class
})
@Configuration(proxyBeanMethods = false)
@Import({ConfigBeans.class, ValidationBeans.class, MessagingBeans.class, StorageBeans.class})
@ImportAutoConfiguration({
ManagementContextAutoConfiguration.class,
ServletManagementContextAutoConfiguration.class,
EndpointAutoConfiguration.class,
WebEndpointAutoConfiguration.class,
HealthEndpointAutoConfiguration.class,
ObservationAutoConfiguration.class,
WebMvcObservationAutoConfiguration.class
})
public class App {

public static void main(String[] args) {
SpringApplication.run(App.class, args);
}

@Bean
@ConditionalOnWebMode(SPRING_MVC)
SepulkaController sepulkaController(SepulkaClient client, SepulkaMsgMapper mapper) {
@ConditionalOnMessageMappingMode(SPRING_WEB_MVC)
SepulkaController sepulkaControllerSpringWeb(SepulkaClient client, SepulkaMsgMapper mapper) {
return new SepulkaController(client, mapper);
}

Expand All @@ -95,13 +78,13 @@ SepulkaConverter sepulkaConverter() {
}

@Bean
@ConditionalOnOrmMode(SPRING_DATA)
@ConditionalOnStateMappingMode(SPRING_DATA)
SepulkaDaoSpringData sepulkaDaoSpringData(SepulkaRecMapper mapper, SepulkaRepository repository) {
return new SepulkaDaoSpringData(mapper, repository);
}

@Bean
@ConditionalOnOrmMode(MY_BATIS)
@ConditionalOnStateMappingMode(MY_BATIS)
SepulkaDaoMyBatis sepulkaDaoMyBatis(SepulkaRecMapper recMapper, SepulkaSqlMapper sqlMapper) {
return new SepulkaDaoMyBatis(recMapper, sqlMapper);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package smecalculus.bezmen.messaging.springmvc;
package smecalculus.bezmen.messaging.springwebmvc;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import smecalculus.bezmen.messaging.SepulkaMsgMapper;
import smecalculus.bezmen.messaging.SepulkaMsgMapperImpl;
import smecalculus.bezmen.messaging.client.SepulkaClient;
import smecalculus.bezmen.messaging.springmvc.SepulkaController;
import smecalculus.bezmen.messaging.springwebmvc.SepulkaController;
import smecalculus.bezmen.validation.EdgeValidator;

@Import(ValidationBeans.class)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package smecalculus.bezmen.construction;

import static java.util.stream.Collectors.joining;
import static smecalculus.bezmen.configuration.OrmMode.MY_BATIS;
import static smecalculus.bezmen.configuration.OrmMode.SPRING_DATA;
import static smecalculus.bezmen.configuration.VendorMode.POSTGRES;
import static smecalculus.bezmen.configuration.StateMappingMode.MY_BATIS;
import static smecalculus.bezmen.configuration.StateMappingMode.SPRING_DATA;
import static smecalculus.bezmen.configuration.StorageProtocolMode.POSTGRES;

import java.util.Collection;
import java.util.List;
Expand All @@ -13,9 +13,9 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import smecalculus.bezmen.configuration.OrmProps;
import smecalculus.bezmen.configuration.StateMappingProps;
import smecalculus.bezmen.configuration.StorageProps;
import smecalculus.bezmen.configuration.VendorProps;
import smecalculus.bezmen.configuration.StorageProtocolProps;
import smecalculus.bezmen.storage.SepulkaDao;
import smecalculus.bezmen.storage.SepulkaDaoMyBatis;
import smecalculus.bezmen.storage.SepulkaDaoSpringData;
Expand All @@ -33,7 +33,7 @@ public class SepulkaDaoBeans {
public DataSource dataSource(StorageProps storageProps) {
List<String> common = List.of("DB_CLOSE_DELAY=-1");
List<String> specific =
switch (storageProps.vendorProps().mode()) {
switch (storageProps.protocolProps().protocolMode()) {
case H2 -> List.of("MODE=STRICT");
case POSTGRES -> List.of("MODE=PostgreSQL", "DATABASE_TO_LOWER=TRUE", "DEFAULT_NULL_ORDERING=HIGH");
};
Expand Down Expand Up @@ -64,8 +64,11 @@ public SepulkaDao underTest(SepulkaRecMapper recMapper, SepulkaRepository reposi
@Bean
public StorageProps storageProps() {
return StorageProps.builder()
.ormProps(OrmProps.builder().mode(SPRING_DATA).build())
.vendorProps(VendorProps.builder().mode(POSTGRES).build())
.mappingProps(
StateMappingProps.builder().mappingMode(SPRING_DATA).build())
.protocolProps(StorageProtocolProps.builder()
.protocolMode(POSTGRES)
.build())
.build();
}
}
Expand All @@ -81,8 +84,11 @@ public SepulkaDao underTest(SepulkaRecMapper recMapper, SepulkaSqlMapper sqlMapp
@Bean
public StorageProps storageProps() {
return StorageProps.builder()
.ormProps(OrmProps.builder().mode(MY_BATIS).build())
.vendorProps(VendorProps.builder().mode(POSTGRES).build())
.mappingProps(
StateMappingProps.builder().mappingMode(MY_BATIS).build())
.protocolProps(StorageProtocolProps.builder()
.protocolMode(POSTGRES)
.build())
.build();
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package smecalculus.bezmen.storage;

import org.springframework.test.context.ContextConfiguration;
import smecalculus.bezmen.construction.OrmMyBatisBeans;
import smecalculus.bezmen.construction.MappingMyBatisBeans;
import smecalculus.bezmen.construction.SepulkaDaoBeans;

@ContextConfiguration(classes = {SepulkaDaoBeans.MyBatisPostgres.class, OrmMyBatisBeans.class})
@ContextConfiguration(classes = {SepulkaDaoBeans.MyBatisPostgres.class, MappingMyBatisBeans.class})
public class SepulkaDaoMyBatisPostgresIT extends SepulkaDaoIT {}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package smecalculus.bezmen.storage;

import org.springframework.test.context.ContextConfiguration;
import smecalculus.bezmen.construction.OrmSpringDataBeans;
import smecalculus.bezmen.construction.MappingSpringDataBeans;
import smecalculus.bezmen.construction.SepulkaDaoBeans;

@ContextConfiguration(classes = {SepulkaDaoBeans.SpringDataPostgres.class, OrmSpringDataBeans.class})
@ContextConfiguration(classes = {SepulkaDaoBeans.SpringDataPostgres.class, MappingSpringDataBeans.class})
public class SepulkaDaoSpringDataPostgresIT extends SepulkaDaoIT {}
Loading