Repository: jhipster/jhipster-lite
Pull request #10406 (2024-07-27 08:32:37)

Cypress tooling
Add tooling for cypress component tests


Patch:
diff --git a/src/main/java/tech/jhipster/lite/generator/client/angular/security/oauth2/domain/AngularOauth2ModuleFactory.java b/src/main/java/tech/jhipster/lite/generator/client/angular/security/oauth2/domain/AngularOauth2ModuleFactory.java
index 02b4cff4a..9a114129a 100644
--- a/src/main/java/tech/jhipster/lite/generator/client/angular/security/oauth2/domain/AngularOauth2ModuleFactory.java
+++ b/src/main/java/tech/jhipster/lite/generator/client/angular/security/oauth2/domain/AngularOauth2ModuleFactory.java
@@ -15,10 +15,10 @@ import tech.jhipster.lite.shared.error.domain.Assert;
 
 public class AngularOauth2ModuleFactory {
 
-  private static final Pattern PROVIDERS_PATTERN = Pattern.compile("(providers: *\\[)");
-  private static final ElementReplacer EXISTING_PROVIDERS_NEEDLE = new RegexReplacer(
-    (contentBeforeReplacement, replacement) -> PROVIDERS_PATTERN.matcher(contentBeforeReplacement).find(),
-    PROVIDERS_PATTERN
+  private static final Pattern PROVIDE_HTTP_CLIENT = Pattern.compile("provideHttpClient\\(\\),");
+  private static final ElementReplacer EXISTING_PROVIDE_HTTP_CLIENT_NEEDLE = new RegexReplacer(
+    (contentBeforeReplacement, replacement) -> PROVIDE_HTTP_CLIENT.matcher(contentBeforeReplacement).find(),
+    PROVIDE_HTTP_CLIENT
   );
 
   private static final ElementReplacer ENVIRONMENT_NEEDLE = lineAfterRegex("export const environment *= *\\{");
@@ -103,7 +103,7 @@ public class AngularOauth2ModuleFactory {
 
   private static final String HTTP_AUTH_INTERCEPTOR_IMPORT =
     """
-    import { HttpAuthInterceptor } from './app/auth/http-auth.interceptor';
+    import { httpAuthInterceptor } from './app/auth/http-auth.interceptor';
     """;
 
   private static final JHipsterSource SOURCE = from("client/angular/security/oauth2/src/main/webapp/app");
@@ -149,7 +149,7 @@ public class AngularOauth2ModuleFactory {
           .add(EMPTY_ALLOWED_COMMON_DEPENDENCIES_NEEDLE, "$1\"keycloak-js\"]")
           .and()
         .in(path("src/main/webapp/main.ts"))
-          .add(EXISTING_PROVIDERS_NEEDLE, "providers: [{ provide: HTTP_INTERCEPTORS, useClass: HttpAuthInterceptor, multi: true },")
+          .add(EXISTING_PROVIDE_HTTP_CLIENT_NEEDLE, "provideHttpClient(withInterceptors([httpAuthInterceptor])),")
           .add(fileStart(), HTTP_AUTH_INTERCEPTOR_IMPORT)
           .and()
         .in(path("src/main/webapp/app/app.component.ts"))
diff --git a/src/main/java/tech/jhipster/lite/generator/client/loader/application/TsLoaderModuleApplicationService.java b/src/main/java/tech/jhipster/lite/generator/client/loader/application/TsLoaderModuleApplicationService.java
new file mode 100644
index 000000000..5af52b48a
--- /dev/null
+++ b/src/main/java/tech/jhipster/lite/generator/client/loader/application/TsLoaderModuleApplicationService.java
@@ -0,0 +1,20 @@
+package tech.jhipster.lite.generator.client.loader.application;
+
+import org.springframework.stereotype.Service;
+import tech.jhipster.lite.generator.client.loader.domain.TsLoaderModuleFactory;
+import tech.jhipster.lite.module.domain.JHipsterModule;
+import tech.jhipster.lite.module.domain.properties.JHipsterModuleProperties;
+
+@Service
+public class TsLoaderModuleApplicationService {
+
+  private final TsLoaderModuleFactory factory;
+
+  public TsLoaderModuleApplicationService() {
+    this.factory = new TsLoaderModuleFactory();
+  }
+
+  public JHipsterModule buildModule(JHipsterModuleProperties properties) {
+    return factory.buildModule(properties);
+  }
+}
diff --git a/src/main/java/tech/jhipster/lite/generator/client/loader/domain/TsLoaderModuleFactory.java b/src/main/java/tech/jhipster/lite/generator/client/loader/domain/TsLoaderModuleFactory.java
new file mode 100644
index 000000000..72f7c075d
--- /dev/null
+++ b/src/main/java/tech/jhipster/lite/generator/client/loader/domain/TsLoaderModuleFactory.java
@@ -0,0 +1,25 @@
+package tech.jhipster.lite.generator.client.loader.domain;
+
+import static tech.jhipster.lite.module.domain.JHipsterModule.*;
+
+import tech.jhipster.lite.module.domain.JHipsterModule;
+import tech.jhipster.lite.module.domain.file.JHipsterSource;
+import tech.jhipster.lite.module.domain.properties.JHipsterModuleProperties;
+
+public class TsLoaderModuleFactory {
+
+  public JHipsterModule buildModule(JHipsterModuleProperties properties) {
+    JHipsterSource source = from("client/loader");
+
+    String destination = "shared/loader/infrastructure/primary";
+
+    //@formatter:off
+    return moduleBuilder(properties)
+      .files()
+        .add(source.template("Loader.ts"), to("src/main/webapp/app").append(destination).append("Loader.ts"))
+        .add(source.template("Loader.spec.ts"), to("src/test/javascript/spec").append(destination).append("Loader.spec.ts"))
+        .and()
+      .build();
+    //@formatter:on
+  }
+}
diff --git a/src/main/java/tech/jhipster/lite/generator/client/loader/infrastructure/primary/TsLoaderModuleConfiguration.java b/src/main/java/tech/jhipster/lite/generator/client/loader/infrastructure/primary/TsLoaderModuleConfiguration.java
new file mode 100644
index 000000000..9a0c2cc6f
--- /dev/null
+++ b/src/main/java/tech/jhipster/lite/generator/client/loader/infrastructure/primary/TsLoaderModuleConfiguration.java
@@ -0,0 +1,23 @@
+package tech.jhipster.lite.generator.client.loader.infrastructure.primary;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import tech.jhipster.lite.generator.client.loader.application.TsLoaderModuleApplicationService;
+import tech.jhipster.lite.generator.slug.domain.JHLiteModuleSlug;
+import tech.jhipster.lite.module.domain.resource.JHipsterModuleOrganization;
+import tech.jhipster.lite.module.domain.resource.JHipsterModuleResource;
+
+@Configuration
+class TsLoaderModuleConfiguration {
+
+  @Bean
+  JHipsterModuleResource tsLoaderModule(TsLoaderModuleApplicationService tsLoader) {
+    return JHipsterModuleResource.builder()
+      .slug(JHLiteModuleSlug.TS_LOADER)
+      .withoutProperties()
+      .apiDoc("Client", "Helper class to represent loading states")
+      .organization(JHipsterModuleOrganization.builder().addDependency(JHLiteModuleSlug.INIT).build())
+      .tags("client")
+      .factory(tsLoader::buildModule);
+  }
+}
diff --git a/src/main/java/tech/jhipster/lite/generator/client/loader/package-info.java b/src/main/java/tech/jhipster/lite/generator/client/loader/package-info.java
new file mode 100644
index 000000000..2fb5b26bc
--- /dev/null
+++ b/src/main/java/tech/jhipster/lite/generator/client/loader/package-info.java
@@ -0,0 +1,2 @@
+@tech.jhipster.lite.SharedKernel
+package tech.jhipster.lite.generator.client.loader;
diff --git a/src/main/java/tech/jhipster/lite/generator/client/pagination_domain/application/TsPaginationDomainApplicationService.java b/src/main/java/tech/jhipster/lite/generator/client/pagination_domain/application/TsPaginationDomainApplicationService.java
new file mode 100644
index 000000000..f969abc00
--- /dev/null
+++ b/src/main/java/tech/jhipster/lite/generator/client/pagination_domain/application/TsPaginationDomainApplicationService.java
@@ -0,0 +1,16 @@
+package tech.jhipster.lite.generator.client.pagination_domain.application;
+
+import org.springframework.stereotype.Service;
+import tech.jhipster.lite.generator.client.pagination_domain.domain.TSPaginationDomainModuleFactory;
+import tech.jhipster.lite.module.domain.JHipsterModule;
+import tech.jhipster.lite.module.domain.properties.JHipsterModuleProperties;
+
+@Service
+public class TsPaginationDomainApplicationService {
+
+  private static final TSPaginationDomainModuleFactory factory = new TSPaginationDomainModuleFactory();
+
+  public JHipsterModule buildModule(JHipsterModuleProperties properties) {
+    return factory.buildModule(properties);
+  }
+}
diff --git a/src/main/java/tech/jhipster/lite/generator/client/pagination_domain/domain/TSPaginationDomainModuleFactory.java b/src/main/java/tech/jhipster/lite/generator/client/pagination_domain/domain/TSPaginationDomainModuleFactory.java
new file mode 100644
index 000000000..7841039ce
--- /dev/null
+++ b/src/main/java/tech/jhipster/lite/generator/client/pagination_domain/domain/TSPaginationDomainModuleFactory.java
@@ -0,0 +1,25 @@
+package tech.jhipster.lite.generator.client.pagination_domain.domain;
+
+import static tech.jhipster.lite.module.domain.JHipsterModule.*;
+
+import tech.jhipster.lite.module.domain.JHipsterModule;
+import tech.jhipster.lite.module.domain.file.JHipsterDestination;
+import tech.jhipster.lite.module.domain.file.JHipsterSource;
+import tech.jhipster.lite.module.domain.properties.JHipsterModuleProperties;
+
+public class TSPaginationDomainModuleFactory {
+
+  public JHipsterModule buildModule(JHipsterModuleProperties properties) {
+    JHipsterSource source = from("client/pagination/domain");
+    JHipsterDestination destination = to("src/main/webapp/app/shared/pagination/domain");
+
+    // @formatter:off
+    return moduleBuilder(properties)
+      .files()
+        .add(source.template("Page.ts"), destination.append("Page.ts"))
+        .add(source.template("DisplayedOnPage.ts"), destination.append("DisplayedOnPage.ts"))
+        .and()
+      .build();
+    //@formatter:on
+  }
+}
diff --git a/src/main/java/tech/jhipster/lite/generator/client/pagination_domain/infrastructure/primary/TsPaginationDomainModuleConfiguration.java b/src/main/java/tech/jhipster/lite/generator/client/pagination_domain/infrastructure/primary/TsPaginationDomainModuleConfiguration.java
new file mode 100644
index 000000000..ec2029fd0
--- /dev/null
+++ b/src/main/java/tech/jhipster/lite/generator/client/pagination_domain/infrastructure/primary/TsPaginationDomainModuleConfiguration.java
@@ -0,0 +1,23 @@
+package tech.jhipster.lite.generator.client.pagination_domain.infrastructure.primary;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import tech.jhipster.lite.generator.client.pagination_domain.application.TsPaginationDomainApplicationService;
+import tech.jhipster.lite.generator.slug.domain.JHLiteModuleSlug;
+import tech.jhipster.lite.module.domain.resource.JHipsterModuleOrganization;
+import tech.jhipster.lite.module.domain.resource.JHipsterModuleResource;
+
+@Configuration
+class TsPaginationDomainModuleConfiguration {
+
+  @Bean
+  JHipsterModuleResource tsPaginationDomainModule(TsPaginationDomainApplicationService tsPaginationDomain) {
+    return JHipsterModuleResource.builder()
+      .slug(JHLiteModuleSlug.TS_PAGINATION_DOMAIN)
+      .withoutProperties()
+      .apiDoc("client", "Add TS domain for pagination")
+      .organization(JHipsterModuleOrganization.builder().addDependency(JHLiteModuleSlug.INIT).build())
+      .tags("client")
+      .factory(tsPaginationDomain::buildModule);
+  }
+}
diff --git a/src/main/java/tech/jhipster/lite/generator/client/pagination_domain/package-info.java b/src/main/java/tech/jhipster/lite/generator/client/pagination_domain/package-info.java
new file mode 100644
index 000000000..a00abbb4e
--- /dev/null
+++ b/src/main/java/tech/jhipster/lite/generator/client/pagination_domain/package-info.java
@@ -0,0 +1,2 @@
+@tech.jhipster.lite.BusinessContext
+package tech.jhipster.lite.generator.client.pagination_domain;
diff --git a/src/main/java/tech/jhipster/lite/generator/client/tools/cypress/domain/CypressModuleFactory.java b/src/main/java/tech/jhipster/lite/generator/client/tools/cypress/domain/CypressModuleFactory.java
index ac9154778..0c1860a81 100644
--- a/src/main/java/tech/jhipster/lite/generator/client/tools/cypress/domain/CypressModuleFactory.java
+++ b/src/main/java/tech/jhipster/lite/generator/client/tools/cypress/domain/CypressModuleFactory.java
@@ -20,6 +20,7 @@ public class CypressModuleFactory {
   private static final JHipsterDestination CYPRESS_DESTINATION = to(CYPRESS_TEST);
 
   private static final String PRIMARY_APP = "common/primary/app";
+  private static final String UTILS = "utils";
 
   private static final String CYPRESS_EXCLUSION = "\"src/test/javascript/integration/**/*.ts\"";
   private static final String EXCLUDE_KEY = "\"exclude\"";
@@ -64,6 +65,10 @@ public class CypressModuleFactory {
           SOURCE.append(CYPRESS_TEST).append(PRIMARY_APP).file("Home.spec.ts"),
           CYPRESS_DESTINATION.append(PRIMARY_APP).append("Home.spec.ts")
         )
+        .batch(SOURCE.append(CYPRESS_TEST).append(UTILS), CYPRESS_DESTINATION.append(UTILS))
+          .addFile("Interceptor.ts")
+          .addFile("DataSelector.ts")
+          .and()
         .and()
       .optionalReplacements()
         .in(path("tsconfig.json"))
diff --git a/src/main/java/tech/jhipster/lite/generator/slug/domain/JHLiteModuleSlug.java b/src/main/java/tech/jhipster/lite/generator/slug/domain/JHLiteModuleSlug.java
index 65469a17d..48173001e 100644
--- a/src/main/java/tech/jhipster/lite/generator/slug/domain/JHLiteModuleSlug.java
+++ b/src/main/java/tech/jhipster/lite/generator/slug/domain/JHLiteModuleSlug.java
@@ -136,13 +136,15 @@ public enum JHLiteModuleSlug implements JHipsterModuleSlugFactory {
   SPRINGDOC_OAUTH_2_AUTH_0("springdoc-oauth2-auth0"),
   SPRINGDOC_OAUTH_2_OKTA("springdoc-oauth2-okta"),
   SPRINGDOC_WEBFLUX_OPENAPI("springdoc-webflux-openapi"),
+  TS_LOADER("ts-loader"),
   WEBJARS_LOCATOR("webjars-locator"),
   HTMX_WEBJARS("htmx-webjars"),
   ALPINE_JS_WEBJARS("alpinejs-webjars"),
   SVELTE_CORE("svelte-core"),
   TYPESCRIPT("typescript"),
   VUE_CORE("vue-core"),
-  VUE_PINIA("vue-pinia");
+  VUE_PINIA("vue-pinia"),
+  TS_PAGINATION_DOMAIN("ts-pagination-domain");
 
   private static final Map<String, JHLiteModuleSlug> moduleSlugMap = Stream.of(values()).collect(
     Collectors.toMap(JHLiteModuleSlug::get, Function.identity())
diff --git a/src/main/resources/generator/client/angular/admin/src/main/webapp/app/admin/health/health.component.spec.ts.mustache b/src/main/resources/generator/client/angular/admin/src/main/webapp/app/admin/health/health.component.spec.ts.mustache
index 395831cdf..cb15515d5 100644
--- a/src/main/resources/generator/client/angular/admin/src/main/webapp/app/admin/health/health.component.spec.ts.mustache
+++ b/src/main/resources/generator/client/angular/admin/src/main/webapp/app/admin/health/health.component.spec.ts.mustache
@@ -33,14 +33,11 @@ describe('HealthComponent', () => {
 
   describe('refresh', () => {
     it('should call refresh on init', () => {
-      // GIVEN
       const health: Health = { status: 'UP', components: { mail: { status: 'UP', details: { mailDetail: 'mail' } } } };
       jest.spyOn(service, 'checkHealth').mockReturnValue(of(health));
 
-      // WHEN
       comp.ngOnInit();
 
-      // THEN
       expect(service.checkHealth).toHaveBeenCalled();
       expect(
         service.checkHealth().subscribe({
@@ -50,31 +47,25 @@ describe('HealthComponent', () => {
     });
 
     it('should call checkHealth on refresh', done => {
-      // GIVEN
       const health: Health = { status: 'UP', components: { mail: { status: 'UP', details: { mailDetail: 'mail' } } } };
       jest.spyOn(service, 'checkHealth').mockImplementation(() => of(health));
 
-      // WHEN
       comp.refresh();
       done();
 
-      // THEN
       expect(service.checkHealth).toHaveBeenCalled();
     });
   });
 
   describe('showHealth', () => {
     it('should open dialog', async () => {
-      // GIVEN
       const health: Health = {
         status: 'UP',
         components: null,
       };
 
-      // WHEN
       comp.showHealth(health);
 
-      // THEN
       const dialogs = await loader.getAllHarnesses(MatDialogHarness);
       expect(dialogs.length).toBe(1);
     });
@@ -82,18 +73,14 @@ describe('HealthComponent', () => {
 
   describe('getBadgeClass', () => {
     it('should get success badge class', () => {
-      // WHEN
       const upBadgeClass = comp.getBadgeClass('UP');
 
-      // THEN
       expect(upBadgeClass).toEqual('bg-success');
     });
 
     it('should get danger badge class', () => {
-      // WHEN
       const downBadgeClass = comp.getBadgeClass('DOWN');
 
-      // THEN
       expect(downBadgeClass).toEqual('bg-danger');
     });
   });
diff --git a/src/main/resources/generator/client/angular/admin/src/main/webapp/app/admin/health/health.service.spec.ts.mustache b/src/main/resources/generator/client/angular/admin/src/main/webapp/app/admin/health/health.service.spec.ts.mustache
index 235b22636..261de4e79 100644
--- a/src/main/resources/generator/client/angular/admin/src/main/webapp/app/admin/health/health.service.spec.ts.mustache
+++ b/src/main/resources/generator/client/angular/admin/src/main/webapp/app/admin/health/health.service.spec.ts.mustache
@@ -26,13 +26,11 @@ describe('HealthService Service', () => {
 
   describe('Service methods', () => {
     it('should call management/health endpoint with correct values', () => {
-      // GIVEN
       let expectedResult;
       const checkHealth = {
         components: [],
       };
 
-      // WHEN
       service.checkHealth().subscribe(received => {
         expectedResult = received;
       });
@@ -42,7 +40,6 @@ describe('HealthService Service', () => {
       });
       testRequest.flush(checkHealth);
 
-      // THEN
       expect(expectedResult).toEqual(checkHealth);
     });
   });
diff --git a/src/main/resources/generator/client/angular/admin/src/main/webapp/app/admin/health/modal/health-modal.component.spec.ts.mustache b/src/main/resources/generator/client/angular/admin/src/main/webapp/app/admin/health/modal/health-modal.component.spec.ts.mustache
index 1b014e6f6..5b65d6554 100644
--- a/src/main/resources/generator/client/angular/admin/src/main/webapp/app/admin/health/modal/health-modal.component.spec.ts.mustache
+++ b/src/main/resources/generator/client/angular/admin/src/main/webapp/app/admin/health/modal/health-modal.component.spec.ts.mustache
@@ -31,72 +31,57 @@ describe('HealthModalComponent', () => {
 
   describe('readableValue', () => {
     it('should return stringify value', () => {
-      // GIVEN
       comp.healthDetails = {
         key: 'hystrix',
         status: 'UP',
       };
 
-      // WHEN
       const result = comp.readableValue({ name: 'jhipster' });
 
-      // THEN
       expect(result).toEqual('{"name":"jhipster"}');
     });
 
     it('should return string value', () => {
-      // GIVEN
       comp.healthDetails = {
         key: 'hystrix',
         status: 'UP',
       };
 
-      // WHEN
       const result = comp.readableValue('jhipster');
 
-      // THEN
       expect(result).toEqual('jhipster');
     });
 
     it('should return storage space in an human readable unit (GB)', () => {
-      // GIVEN
       comp.healthDetails = {
         key: 'diskSpace',
         status: 'UP',
       };
 
-      // WHEN
       const result = comp.readableValue(1073741825);
 
-      // THEN
       expect(result).toEqual('1.00 GB');
     });
 
     it('should return storage space in an human readable unit (MB)', () => {
-      // GIVEN
       comp.healthDetails = {
         key: 'diskSpace',
         status: 'UP',
       };
 
-      // WHEN
       const result = comp.readableValue(1073741824);
 
-      // THEN
       expect(result).toEqual('1024.00 MB');
     });
 
     it('should return string value', () => {
-      // GIVEN
       comp.healthDetails = {
         key: 'mail',
         status: 'UP',
       };
 
-      // WHEN
       const result = comp.readableValue(1234);
 
-      // THEN
       expect(result).toEqual('1234');
     });
   });
diff --git a/src/main/resources/generator/client/angular/core/src/main/webapp/app/app.component.spec.ts.mustache b/src/main/resources/generator/client/angular/core/src/main/webapp/app/app.component.spec.ts.mustache
index 1b7a2fa38..be331a28d 100644
--- a/src/main/resources/generator/client/angular/core/src/main/webapp/app/app.component.spec.ts.mustache
+++ b/src/main/resources/generator/client/angular/core/src/main/webapp/app/app.component.spec.ts.mustache
@@ -20,10 +20,8 @@ describe('App Component', () => {
 
   describe('ngOnInit', () => {
     it('should have appName', () => {
-      // WHEN
       fixture.detectChanges();
 
-      // THEN
       expect(comp.appName()).toEqual('{{baseName}}');
     });
   });
diff --git a/src/main/resources/generator/client/angular/core/src/main/webapp/main.ts.mustache b/src/main/resources/generator/client/angular/core/src/main/webapp/main.ts.mustache
index d194f646c..66980d6d6 100644
--- a/src/main/resources/generator/client/angular/core/src/main/webapp/main.ts.mustache
+++ b/src/main/resources/generator/client/angular/core/src/main/webapp/main.ts.mustache
@@ -1,4 +1,4 @@
-import { HTTP_INTERCEPTORS, provideHttpClient, withInterceptors } from '@angular/common/http';
+import { provideHttpClient, withInterceptors } from '@angular/common/http';
 import { enableProdMode, importProvidersFrom } from '@angular/core';
 import { REMOVE_STYLES_ON_COMPONENT_DESTROY, bootstrapApplication } from '@angular/platform-browser';
 import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
diff --git a/src/main/resources/generator/client/angular/security/jwt/src/main/webapp/app/auth/account.model.spec.ts.mustache b/src/main/resources/generator/client/angular/security/jwt/src/main/webapp/app/auth/account.model.spec.ts.mustache
index 8e6cbe1b1..266f1307f 100644
--- a/src/main/resources/generator/client/angular/security/jwt/src/main/webapp/app/auth/account.model.spec.ts.mustache
+++ b/src/main/resources/generator/client/angular/security/jwt/src/main/webapp/app/auth/account.model.spec.ts.mustache
@@ -2,7 +2,6 @@ import { Account } from './account.model';
 
 describe('AccountModel', () => {
   it('should initialize an account model', () => {
-    // GIVEN
     const activated = true;
     const authorities = ['ADMIN'];
     const email = 'jdoe@test.com';
@@ -11,10 +10,8 @@ describe('AccountModel', () => {
     const lastName = 'Doe';
     const login = 'jdoe';
 
-    // WHEN
     const account: Account = new Account(activated, authorities, email, firstName, langKey, lastName, login);
 
-    // THEN
     expect(account.activated).toBe(activated);
     expect(account.authorities).toBe(authorities);
     expect(account.email).toBe(email);
diff --git a/src/main/resources/generator/client/angular/security/jwt/src/main/webapp/app/auth/account.service.spec.ts.mustache b/src/main/resources/generator/client/angular/security/jwt/src/main/webapp/app/auth/account.service.spec.ts.mustache
index 0f6b6eb2f..97e0e70ee 100644
--- a/src/main/resources/generator/client/angular/security/jwt/src/main/webapp/app/auth/account.service.spec.ts.mustache
+++ b/src/main/resources/generator/client/angular/security/jwt/src/main/webapp/app/auth/account.service.spec.ts.mustache
@@ -43,69 +43,53 @@ describe('Account Service', () => {
 
   describe('authenticate', () => {
     it('authenticationState should emit null if input is null', () => {
-      // GIVEN
       let userIdentity: Account | null = accountWithAuthorities([]);
       service.getAuthenticationState().subscribe(account => (userIdentity = account));
 
-      // WHEN
       service.authenticate(null);
 
-      // THEN
       expect(userIdentity).toBeNull();
       expect(service.isAuthenticated()).toBe(false);
     });
 
     it('authenticationState should emit the same account as was in input parameter', () => {
-      // GIVEN
       const expectedResult = accountWithAuthorities([]);
       let userIdentity: Account | null = null;
       service.getAuthenticationState().subscribe(account => (userIdentity = account));
 
-      // WHEN
       service.authenticate(expectedResult);
 
-      // THEN
       expect(userIdentity).toEqual(expectedResult);
     });
   });
 
   describe('identity', () => {
     it('should handle error when /account is on error', () => {
-      // When I call
       service.identity(false).subscribe();
 
-      // Then there is a new error request
       httpMock.expectOne({ method: 'GET' }).flush('404 error', { status: 404, statusText: 'Not Found' });
     });
 
     it('should call /account only once if last call have not returned', () => {
-      // When I call
       service.identity().subscribe();
 
-      // Once more
       service.identity().subscribe();
 
-      // Then there is only request
       httpMock.expectOne({ method: 'GET' });
     });
 
     it('should call /account only once if not logged out after first authentication and should call /account again if user has logged out', () => {
-      // Given the user is authenticated
       service.identity().subscribe();
       httpMock.expectOne({ method: 'GET' }).flush({});
 
-      // When I call
       service.identity().subscribe();
 
-      // Then there is no second request
       httpMock.expectNone({ method: 'GET' });
 
-      // When I log out
       service.authenticate(null);
-      // and then call
+
       service.identity().subscribe();
 
-      // Then there is a new request
       httpMock.expectOne({ method: 'GET' });
     });
   });
diff --git a/src/main/resources/generator/client/angular/security/jwt/src/main/webapp/app/auth/auth-jwt.service.spec.ts.mustache b/src/main/resources/generator/client/angular/security/jwt/src/main/webapp/app/auth/auth-jwt.service.spec.ts.mustache
index 3a5ac5412..f03f1225c 100644
--- a/src/main/resources/generator/client/angular/security/jwt/src/main/webapp/app/auth/auth-jwt.service.spec.ts.mustache
+++ b/src/main/resources/generator/client/angular/security/jwt/src/main/webapp/app/auth/auth-jwt.service.spec.ts.mustache
@@ -18,14 +18,11 @@ describe('Auth JWT', () => {
 
   describe('Login', () => {
     it('should clear session storage and save in local storage', () => {
-      // GIVEN
       jest.spyOn(Storage.prototype, 'setItem');
 
-      // WHEN
       service.login({ username: 'John', password: '123' }).subscribe();
       httpMock.expectOne('api/authenticate').flush({ id_token: '1' });
 
-      // THEN
       httpMock.verify();
       expect(localStorage.setItem).toHaveBeenCalledWith('authenticationToken', '1');
     });
@@ -33,13 +30,10 @@ describe('Auth JWT', () => {
 
   describe('Logout', () => {
     it('should clear storage', () => {
-      // GIVEN
       jest.spyOn(Storage.prototype, 'removeItem');
 
-      // WHEN
       service.logout().subscribe();
 
-      // THEN
       expect(localStorage.removeItem).toHaveBeenCalledWith('authenticationToken');
     });
   });
diff --git a/src/main/resources/generator/client/angular/security/jwt/src/main/webapp/app/auth/auth.interceptor.spec.ts.mustache b/src/main/resources/generator/client/angular/security/jwt/src/main/webapp/app/auth/auth.interceptor.spec.ts.mustache
index 9122fbf82..f551f310e 100644
--- a/src/main/resources/generator/client/angular/security/jwt/src/main/webapp/app/auth/auth.interceptor.spec.ts.mustache
+++ b/src/main/resources/generator/client/angular/security/jwt/src/main/webapp/app/auth/auth.interceptor.spec.ts.mustache
@@ -24,14 +24,11 @@ describe('Auth Interceptor', () => {
 
   describe('intercept', () => {
     it('should add authorization header with localStorageService', () => {
-      // GIVEN
       const token = 'azerty';
       Storage.prototype.getItem = jest.fn(() => token);
 
-      // WHEN
       service.login({ username: 'John', password: '123' }).subscribe();
 
-      // THEN
       const httpReq = httpMock.expectOne('api/authenticate');
       expect(httpReq.request.headers.has('Authorization')).toBe(true);
     });
diff --git a/src/main/resources/generator/client/angular/security/jwt/src/main/webapp/app/login/login.component.spec.ts.mustache b/src/main/resources/generator/client/angular/security/jwt/src/main/webapp/app/login/login.component.spec.ts.mustache
index 6f9564f5f..f35ce3593 100644
--- a/src/main/resources/generator/client/angular/security/jwt/src/main/webapp/app/login/login.component.spec.ts.mustache
+++ b/src/main/resources/generator/client/angular/security/jwt/src/main/webapp/app/login/login.component.spec.ts.mustache
@@ -53,36 +53,27 @@ describe('Login Component', () => {
 
   describe('ngOnInit', () => {
     it('should check authentication', () => {
-      // GIVEN
       const authenticationState = new Subject<Account | null>();
       mockAccountService.getAuthenticationState = jest.fn(() => authenticationState.asObservable());
 
-      // WHEN
       comp.ngOnInit();
 
-      // THEN
       expect(mockAccountService.getAuthenticationState).toHaveBeenCalled();
 
-      // THEN
       expect(comp.account()).toBeNull();
 
-      // WHEN
       authenticationState.next(account);
 
-      // THEN
       expect(comp.account()).toEqual(account);
 
-      // WHEN
       authenticationState.next(null);
 
-      // THEN
       expect(comp.account()).toBeNull();
     });
   });
 
   describe('login', () => {
     it('should authenticate the user', () => {
-      // GIVEN
       const credentials = {
         username: 'admin',
         password: 'admin',
@@ -93,32 +84,25 @@ describe('Login Component', () => {
         password: 'admin',
       });
 
-      // WHEN
       comp.login();
 
-      // THEN
       expect(mockLoginService.login).toHaveBeenCalledWith(credentials);
     });
 
     it('should not authenticate the user', () => {
-      // GIVEN
       const err = { status: 403 };
       jest.spyOn(mockLoginService, 'login').mockReturnValue(throwError(() => err));
 
-      // WHEN
       comp.login();
 
-      // THEN
       expect(comp.errorMessage()).toEqual('Authentication failed');
     });
   });
 
   describe('logout', () => {
     it('should logout the user', () => {
-      // WHEN
       comp.logout();
 
-      // THEN
       expect(mockLoginService.logout).toHaveBeenCalled();
     });
   });
diff --git a/src/main/resources/generator/client/angular/security/jwt/src/main/webapp/app/login/login.service.spec.ts.mustache b/src/main/resources/generator/client/angular/security/jwt/src/main/webapp/app/login/login.service.spec.ts.mustache
index 1fff25fae..c8c0c1b9b 100644
--- a/src/main/resources/generator/client/angular/security/jwt/src/main/webapp/app/login/login.service.spec.ts.mustache
+++ b/src/main/resources/generator/client/angular/security/jwt/src/main/webapp/app/login/login.service.spec.ts.mustache
@@ -31,7 +31,6 @@ describe('Login Service', () => {
 
   describe('login', () => {
     it('should login', () => {
-      // GIVEN
       mockAccountService.identity = jest.fn(() => of(null));
       const login = new Subject<void>();
       authServerProvider.login = jest.fn(() => login.asObservable());
@@ -41,11 +40,9 @@ describe('Login Service', () => {
         password: 'admin',
       };
 
-      // WHEN
       service.login(credentials).subscribe();
       login.next();
 
-      // THEN
       expect(authServerProvider.login).toHaveBeenCalledWith(credentials);
       expect(mockAccountService.identity).toHaveBeenCalled();
     });
@@ -53,13 +50,10 @@ describe('Login Service', () => {
 
   describe('logout', () => {
     it('should logout', () => {
-      // GIVEN
       authServerProvider.logout = jest.fn(() => of());
 
-      // WHEN
       service.logout();
 
-      // THEN
       expect(authServerProvider.logout).toHaveBeenCalled();
     });
   });
diff --git a/src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/auth/http-auth.interceptor.spec.ts.mustache b/src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/auth/http-auth.interceptor.spec.ts.mustache
index 2a4a39373..b79208651 100644
--- a/src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/auth/http-auth.interceptor.spec.ts.mustache
+++ b/src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/auth/http-auth.interceptor.spec.ts.mustache
@@ -1,12 +1,10 @@
 import { TestBed } from '@angular/core/testing';
-import { HttpHandler, HttpRequest } from '@angular/common/http';
-import { lastValueFrom, of } from 'rxjs';
+import { HttpClient, HttpRequest, provideHttpClient, withInterceptors } from '@angular/common/http';
+import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
 
-import { HttpAuthInterceptor } from './http-auth.interceptor';
+import { httpAuthInterceptor } from './http-auth.interceptor';
 import { Oauth2AuthService } from './oauth2-auth.service';
 
-import Mock = jest.Mock;
-
 const URL = 'http://localhost:8080/api/dummy';
 const HTTP_METHOD = 'GET';
 const TOKEN = '1a2b3c';
@@ -18,58 +16,44 @@ const buildHttpRequest = () => {
   });
 };
 
-describe('HttpAuthInterceptor', () => {
-  let interceptor: HttpAuthInterceptor;
+describe('httpAuthInterceptor', () => {
+  let httpTestingController: HttpTestingController;
+  let httpClient: HttpClient;
   let oauth2AuthService: Oauth2AuthService;
 
   beforeEach(() => {
     TestBed.configureTestingModule({
-      providers: [HttpAuthInterceptor, Oauth2AuthService],
+      providers: [provideHttpClient(withInterceptors([httpAuthInterceptor])), provideHttpClientTesting(), Oauth2AuthService],
     });
-
-    interceptor = TestBed.inject(HttpAuthInterceptor);
+    httpTestingController = TestBed.inject(HttpTestingController);
+    httpClient = TestBed.inject(HttpClient);
     oauth2AuthService = TestBed.inject(Oauth2AuthService);
   });
 
-  it('should be created', () => {
-    expect(interceptor).toBeTruthy();
-  });
-
   describe('Bearer Token', () => {
-    let httpHandler: HttpHandler;
-    let httpHandlerNextMock: Mock;
-
-    beforeEach(() => {
-      httpHandlerNextMock = jest.fn().mockReturnValue(of(null));
-      httpHandler = {
-        handle: httpHandlerNextMock,
-      } as unknown as HttpHandler;
-    });
 
     it('should add authorization bearer token in request when defined', async () => {
       jest.spyOn(oauth2AuthService, 'token', 'get').mockReturnValue(TOKEN);
       const originalRequest = buildHttpRequest();
 
-      await lastValueFrom(interceptor.intercept(originalRequest, httpHandler));
+      httpClient.request(originalRequest).subscribe();
 
-      expect(httpHandler.handle).toHaveBeenCalledTimes(1);
-      const request: HttpRequest<unknown> = httpHandlerNextMock.mock.calls[0][0];
-      expect(request.method).toEqual(HTTP_METHOD);
-      expect(request.url).toEqual(URL);
-      expect(request.headers.get('ContentType')).toEqual('application/json');
-      expect(request.headers.get('Authorization')).toEqual(`Bearer ${TOKEN}`);
+      const req = httpTestingController.expectOne(originalRequest.url);
+      expect(req.request.method).toEqual(HTTP_METHOD);
+      expect(req.request.url).toEqual(URL);
+      expect(req.request.headers.get('ContentType')).toEqual('application/json');
+      expect(req.request.headers.get('Authorization')).toEqual(`Bearer ${TOKEN}`);
     });
 
     it('should not add authorization bearer token in request when it is not defined', async () => {
       jest.spyOn(oauth2AuthService, 'token', 'get').mockReturnValue(undefined);
       const originalRequest: HttpRequest<unknown> = buildHttpRequest();
 
-      await lastValueFrom(interceptor.intercept(originalRequest, httpHandler));
+      httpClient.request(originalRequest).subscribe();
 
-      expect(httpHandler.handle).toHaveBeenCalledTimes(1);
-      const request: HttpRequest<unknown> = httpHandlerNextMock.mock.calls[0][0];
-      expect(request.headers.get('ContentType')).toEqual('application/json');
-      expect(request.headers.get('Authorization')).toBeFalsy();
+      const req = httpTestingController.expectOne(originalRequest.url);
+      expect(req.request.headers.get('ContentType')).toEqual('application/json');
+      expect(req.request.headers.get('Authorization')).toBeFalsy();
     });
   });
 });
diff --git a/src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/auth/http-auth.interceptor.ts.mustache b/src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/auth/http-auth.interceptor.ts.mustache
index 525f19fae..b7a392ba9 100644
--- a/src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/auth/http-auth.interceptor.ts.mustache
+++ b/src/main/resources/generator/client/angular/security/oauth2/src/main/webapp/app/auth/http-auth.interceptor.ts.mustache
@@ -1,21 +1,18 @@
-import { inject, Injectable } from '@angular/core';
-import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
+import { inject } from '@angular/core';
+import { HttpRequest, HttpEvent, HttpHandlerFn } from '@angular/common/http';
 import { Observable } from 'rxjs';
 import { Oauth2AuthService } from './oauth2-auth.service';
 
-@Injectable()
-export class HttpAuthInterceptor implements HttpInterceptor {
-  private readonly oauth2AuthService = inject(Oauth2AuthService);
+export function httpAuthInterceptor(req: HttpRequest<unknown>, next: HttpHandlerFn): Observable<HttpEvent<unknown>> {
+  const oauth2AuthService = inject(Oauth2AuthService);
 
-  intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
-    const token = this.oauth2AuthService.token;
-    if (token) {
-      request = request.clone({
-        setHeaders: {
-          Authorization: `Bearer ${token}`,
-        },
-      });
-    }
-    return next.handle(request);
+  const token = oauth2AuthService.token;
+  if (token) {
+    req = req.clone({
+      setHeaders: {
+        Authorization: `Bearer ${token}`,
+      },
+    });
   }
+  return next(req);
 }
diff --git a/src/main/resources/generator/client/loader/Loader.spec.ts.mustache b/src/main/resources/generator/client/loader/Loader.spec.ts.mustache
new file mode 100644
index 000000000..1195284e1
--- /dev/null
+++ b/src/main/resources/generator/client/loader/Loader.spec.ts.mustache
@@ -0,0 +1,55 @@
+import { describe, expect, it } from 'vitest';
+import { Loader, LoadingState } from '@/shared/loader/infrastructure/primary/Loader.ts';
+
+describe('Loader', () => {
+  describe('Loading loader', () => {
+    const loader = Loader.loading<string>();
+
+    it('should be in a loading state', () => {
+      expect(loader.state()).toBe(LoadingState.LOADING);
+      expect(loader.isLoading()).toBe(true);
+      expect(loader.isLoaded()).toBe(false);
+      expect(loader.isInError()).toBe(false);
+    });
+
+    it('should not get value', () => {
+      expect(() => loader.value()).toThrowError();
+    });
+
+    it('should not get error', () => {
+      expect(() => loader.error()).toThrowError();
+    });
+  });
+
+  describe('Loaded loader', () => {
+    const loader = Loader.loaded('value');
+
+    it('should be loaded', () => {
+      expect(loader.state()).toBe(LoadingState.LOADED);
+      expect(loader.isLoading()).toBe(false);
+      expect(loader.isLoaded()).toBe(true);
+      expect(loader.isInError()).toBe(false);
+      expect(loader.value()).toBe('value');
+    });
+
+    it('should not get error', () => {
+      expect(() => loader.error()).toThrowError();
+    });
+  });
+
+  describe('Error loader', () => {
+    const loader = Loader.error(new Error('oops'));
+
+    it('should be in error state', () => {
+      expect(loader.state()).toBe(LoadingState.ERROR);
+      expect(loader.isLoading()).toBe(false);
+      expect(loader.isLoaded()).toBe(false);
+      expect(loader.isInError()).toBe(true);
+      expect(loader.error().message).toBe('oops');
+    });
+
+    it('should not get value', () => {
+      expect(() => loader.value()).toThrowError();
+    });
+  });
+});
diff --git a/src/main/resources/generator/client/loader/Loader.ts.mustache b/src/main/resources/generator/client/loader/Loader.ts.mustache
new file mode 100644
index 000000000..aada05a63
--- /dev/null
+++ b/src/main/resources/generator/client/loader/Loader.ts.mustache
@@ -0,0 +1,87 @@
+export abstract class Loader<T> {
+  static loading<T>(): Loader<T> {
+    return new LoadingLoader();
+  }
+
+  static loaded<T>(value: T): Loader<T> {
+    return new LoadedLoader(value);
+  }
+
+  static error<T>(error: Error): Loader<T> {
+    return new ErrorLoader(error);
+  }
+
+  isLoading(): boolean {
+    return this.state() === LoadingState.LOADING;
+  }
+
+  isLoaded(): boolean {
+    return this.state() === LoadingState.LOADED;
+  }
+
+  isInError(): boolean {
+    return this.state() === LoadingState.ERROR;
+  }
+
+  abstract state(): LoadingState;
+
+  abstract value(): T;
+
+  abstract error(): Error;
+}
+
+export enum LoadingState {
+  LOADING,
+  LOADED,
+  ERROR,
+}
+
+class LoadingLoader<T> extends Loader<T> {
+  state(): LoadingState {
+    return LoadingState.LOADING;
+  }
+
+  value(): T {
+    throw new Error("Can't get value for a loading loaded");
+  }
+
+  error(): Error {
+    throw new Error("Can't get error for loading loader");
+  }
+}
+
+class LoadedLoader<T> extends Loader<T> {
+  constructor(private readonly _value: T) {
+    super();
+  }
+
+  state(): LoadingState {
+    return LoadingState.LOADED;
+  }
+
+  value(): T {
+    return this._value;
+  }
+
+  error(): Error {
+    throw new Error("Can't get error for loaded loader");
+  }
+}
+
+class ErrorLoader<T> extends Loader<T> {
+  constructor(private readonly _error: Error) {
+    super();
+  }
+
+  state(): LoadingState {
+    return LoadingState.ERROR;
+  }
+
+  value(): T {
+    throw new Error("Can't get value for error loader");
+  }
+
+  error(): Error {
+    return this._error;
+  }
+}
diff --git a/src/main/resources/generator/client/pagination/domain/DisplayedOnPage.ts.mustache b/src/main/resources/generator/client/pagination/domain/DisplayedOnPage.ts.mustache
new file mode 100644
index 000000000..32615d8d5
--- /dev/null
+++ b/src/main/resources/generator/client/pagination/domain/DisplayedOnPage.ts.mustache
@@ -0,0 +1,4 @@
+export type DisplayedOnPage = {
+  start: number;
+  end: number;
+};
diff --git a/src/main/resources/generator/client/pagination/domain/Page.ts.mustache b/src/main/resources/generator/client/pagination/domain/Page.ts.mustache
new file mode 100755
index 000000000..0983280f1
--- /dev/null
+++ b/src/main/resources/generator/client/pagination/domain/Page.ts.mustache
@@ -0,0 +1,11 @@
+import { DisplayedOnPage } from './DisplayedOnPage';
+
+export type Page<Content> = {
+  currentPage: number;
+  pagesCount: number;
+  hasPrevious: boolean;
+  hasNext: boolean;
+  totalElementsCount: number;
+  displayed: DisplayedOnPage;
+  content: Content[];
+};
diff --git a/src/main/resources/generator/dependencies/common/package.json b/src/main/resources/generator/dependencies/common/package.json
index b70a6530f..ad2258c01 100644
--- a/src/main/resources/generator/dependencies/common/package.json
+++ b/src/main/resources/generator/dependencies/common/package.json
@@ -8,7 +8,7 @@
     "@playwright/test": "1.45.3",
     "@prettier/plugin-xml": "3.4.1",
     "@types/jest": "29.5.12",
-    "@types/node": "20.14.11",
+    "@types/node": "20.14.12",
     "@typescript-eslint/eslint-plugin": "7.17.0",
     "@typescript-eslint/parser": "7.17.0",
     "@vitest/coverage-istanbul": "2.0.4",
diff --git a/src/main/webapp/app/common/domain/alert/Alerted.ts b/src/main/webapp/app/common/domain/alert/Alerted.ts
deleted file mode 100644
index 73a35fe77..000000000
--- a/src/main/webapp/app/common/domain/alert/Alerted.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-import { AlertMessage } from '@/common/domain/alert/AlertMessage';
-
-export type Alerted = (message: AlertMessage) => void;
diff --git a/src/main/webapp/app/common/primary/app/App.component.ts b/src/main/webapp/app/common/primary/app/App.component.ts
deleted file mode 100644
index 5b993502b..000000000
--- a/src/main/webapp/app/common/primary/app/App.component.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { HeaderVue } from '@/common/primary/header';
-import { ToastVue } from '@/common/primary/toast';
-
-export default {
-  name: 'App',
-  components: {
-    ToastVue,
-    HeaderVue,
-  },
-};
diff --git a/src/main/webapp/app/main.ts b/src/main/webapp/app/main.ts
index cd960ac4f..c0e4d012b 100644
--- a/src/main/webapp/app/main.ts
+++ b/src/main/webapp/app/main.ts
@@ -1,19 +1,19 @@
-import { AxiosHttp } from '@/http/AxiosHttp';
+import { AxiosHttp } from '@/shared/http/infrastructure/secondary/AxiosHttp';
 import router from '@/router';
 import axios from 'axios';
 import { createApp } from 'vue';
-import App from '@/common/primary/app/App.vue';
-import ConsoleLogger from '@/common/secondary/ConsoleLogger';
-import { MittAlertBus } from '@/common/secondary/alert/MittAlertBus';
+import App from '@/root/infrastructure/primary/App.vue';
+import ConsoleLogger from '@/shared/logger/infrastructure/secondary/ConsoleLogger';
+import { MittAlertBus } from '@/shared/alert/infrastructure/secondary/MittAlertBus';
 import mitt from 'mitt';
 
-import { MittAlertListener } from '@/common/secondary/alert/MittAlertListener';
+import { MittAlertListener } from '@/shared/alert/infrastructure/secondary/MittAlertListener';
 import { RestModulesRepository } from './module/secondary/RestModulesRepository';
 import { RestProjectFoldersRepository } from '@/module/secondary/RestProjectFoldersRepository';
 import { RestManagementRepository } from '@/module/secondary/RestManagementRepository';
-import { WindowApplicationListener } from './common/primary/applicationlistener/WindowApplicationListener';
-import { Timeout } from '@/common/primary/timeout/Timeout';
-import { BodyCursorUpdater } from '@/common/primary/cursor/BodyCursorUpdater';
+import { WindowApplicationListener } from './shared/alert/infrastructure/primary/WindowApplicationListener';
+import { Timeout } from '@/shared/toast/infrastructure/primary/Timeout';
+import { BodyCursorUpdater } from '@/module/primary/landscape/BodyCursorUpdater';
 import { LandscapeScroller } from '@/module/primary/landscape/LandscapeScroller';
 import { LocalWindowThemeRepository } from './module/secondary/LocalWindowThemeRepository';
 import { LocalStorageModuleParametersRepository } from './module/secondary/LocalStorageModuleParametersRepository';
diff --git a/src/main/webapp/app/module/domain/landscape/Landscape.ts b/src/main/webapp/app/module/domain/landscape/Landscape.ts
index 342e84d9b..8a5d12624 100644
--- a/src/main/webapp/app/module/domain/landscape/Landscape.ts
+++ b/src/main/webapp/app/module/domain/landscape/Landscape.ts
@@ -1,5 +1,5 @@
-import { Memoizer } from '@/common/domain/Memoizer';
-import { Optional } from '@/common/domain/Optional';
+import { Memoizer } from '@/shared/memoizer/domain/Memoizer';
+import { Optional } from '@/shared/optional/domain/Optional';
 import { ModulePropertyDefinition } from '../ModulePropertyDefinition';
 import { ModuleSlug } from '../ModuleSlug';
 import { LandscapeElementId } from './LandscapeElementId';
diff --git a/src/main/webapp/app/module/domain/landscape/LandscapeSelectionTree.ts b/src/main/webapp/app/module/domain/landscape/LandscapeSelectionTree.ts
index 3bb725c08..ea8d5abaa 100644
--- a/src/main/webapp/app/module/domain/landscape/LandscapeSelectionTree.ts
+++ b/src/main/webapp/app/module/domain/landscape/LandscapeSelectionTree.ts
@@ -1,4 +1,4 @@
-import { Optional } from '@/common/domain/Optional';
+import { Optional } from '@/shared/optional/domain/Optional';
 import { LandscapeElementId } from './LandscapeElementId';
 import { LandscapeSelectionElement } from './LandscapeSelectionElement';
 
diff --git a/src/main/webapp/app/module/primary/landscape-minimap/LandscapeMiniMap.component.ts b/src/main/webapp/app/module/primary/landscape-minimap/LandscapeMiniMap.component.ts
index 37e107f8f..36774b41b 100644
--- a/src/main/webapp/app/module/primary/landscape-minimap/LandscapeMiniMap.component.ts
+++ b/src/main/webapp/app/module/primary/landscape-minimap/LandscapeMiniMap.component.ts
@@ -1,5 +1,5 @@
 import { defineComponent, onMounted, PropType, ref, Ref } from 'vue';
-import { IconVue } from '@/common/primary/icon';
+import { IconVue } from '@/shared/icon/infrastructure/primary';
 
 export default defineComponent({
   name: 'LandscapeMiniMapComponentVue',
diff --git a/src/main/webapp/app/module/primary/landscape-module/LandscapeModule.component.ts b/src/main/webapp/app/module/primary/landscape-module/LandscapeModule.component.ts
index a6bc9ca1f..3d6c8f121 100644
--- a/src/main/webapp/app/module/primary/landscape-module/LandscapeModule.component.ts
+++ b/src/main/webapp/app/module/primary/landscape-module/LandscapeModule.component.ts
@@ -1,5 +1,5 @@
 import { LandscapeModule } from '@/module/domain/landscape/LandscapeModule';
-import { IconVue } from '@/common/primary/icon';
+import { IconVue } from '@/shared/icon/infrastructure/primary';
 import { defineComponent, PropType } from 'vue';
 
 export default defineComponent({
diff --git a/src/main/webapp/app/common/primary/cursor/BodyCursorUpdater.ts b/src/main/webapp/app/module/primary/landscape/BodyCursorUpdater.ts
similarity index 77%
rename from src/main/webapp/app/common/primary/cursor/BodyCursorUpdater.ts
rename to src/main/webapp/app/module/primary/landscape/BodyCursorUpdater.ts
index 2e70a3bae..43083ccba 100644
--- a/src/main/webapp/app/common/primary/cursor/BodyCursorUpdater.ts
+++ b/src/main/webapp/app/module/primary/landscape/BodyCursorUpdater.ts
@@ -1,4 +1,4 @@
-import { CursorType } from '@/common/primary/cursor/CursorType';
+import { CursorType } from '@/module/primary/landscape/CursorType';
 
 export class BodyCursorUpdater {
   constructor(private window: Window) {}
diff --git a/src/main/webapp/app/common/primary/cursor/CursorType.ts b/src/main/webapp/app/module/primary/landscape/CursorType.ts
similarity index 100%
rename from src/main/webapp/app/common/primary/cursor/CursorType.ts
rename to src/main/webapp/app/module/primary/landscape/CursorType.ts
diff --git a/src/main/webapp/app/module/primary/landscape/Landscape.component.ts b/src/main/webapp/app/module/primary/landscape/Landscape.component.ts
index 2db084f99..b09a13950 100644
--- a/src/main/webapp/app/module/primary/landscape/Landscape.component.ts
+++ b/src/main/webapp/app/module/primary/landscape/Landscape.component.ts
@@ -1,5 +1,5 @@
-import { ApplicationListener } from '@/common/primary/applicationlistener/ApplicationListener';
-import { Loader } from '@/loader/primary/Loader';
+import { ApplicationListener } from '@/shared/alert/infrastructure/primary/ApplicationListener';
+import { Loader } from '@/shared/loader/infrastructure/primary/Loader';
 import { ModulesRepository } from '@/module/domain/ModulesRepository';
 import { defineComponent, inject, nextTick, onBeforeUnmount, onMounted, Ref, ref } from 'vue';
 import { LandscapeModuleVue } from '../landscape-module';
@@ -10,14 +10,14 @@ import { DisplayMode } from './DisplayMode';
 import { emptyLandscapeSize, LandscapeConnectorsSize } from './LandscapeConnectorsSize';
 import { ModulePropertiesFormVue } from '../module-properties-form';
 import { ModulePropertyDefinition } from '@/module/domain/ModulePropertyDefinition';
-import { AlertBus } from '@/common/domain/alert/AlertBus';
+import { AlertBus } from '@/shared/alert/domain/AlertBus';
 import { ProjectHistory } from '@/module/domain/ProjectHistory';
 import { ProjectFoldersRepository } from '@/module/domain/ProjectFoldersRepository';
 import { ProjectActionsVue } from '../project-actions';
 import { castValue, empty } from '../PropertyValue';
 import { ModuleParameter } from '@/module/domain/ModuleParameter';
 import { Landscape } from '@/module/domain/landscape/Landscape';
-import { IconVue } from '@/common/primary/icon';
+import { IconVue } from '@/shared/icon/infrastructure/primary';
 import { ModuleSlug } from '@/module/domain/ModuleSlug';
 import { LandscapeLevel } from '@/module/domain/landscape/LandscapeLevel';
 import { LandscapeModule } from '@/module/domain/landscape/LandscapeModule';
@@ -26,7 +26,7 @@ import { LandscapeElement } from '@/module/domain/landscape/LandscapeElement';
 import { LandscapeFeature } from '@/module/domain/landscape/LandscapeFeature';
 import { LandscapeElementId } from '@/module/domain/landscape/LandscapeElementId';
 import { LandscapeFeatureSlug } from '@/module/domain/landscape/LandscapeFeatureSlug';
-import { BodyCursorUpdater } from '@/common/primary/cursor/BodyCursorUpdater';
+import { BodyCursorUpdater } from '@/module/primary/landscape/BodyCursorUpdater';
 import { LandscapeScroller } from '@/module/primary/landscape/LandscapeScroller';
 import { ModuleParametersRepository } from '@/module/domain/ModuleParametersRepository';
 import { LandscapeNavigation } from './LandscapeNavigation';
@@ -34,7 +34,14 @@ import { AnchorPointState } from '@/module/domain/AnchorPointState';
 
 export default defineComponent({
   name: 'LandscapeVue',
-  components: { LandscapeModuleVue, ModulePropertiesFormVue, ProjectActionsVue, IconVue, LandscapeLoaderVue, LandscapeMiniMapVue },
+  components: {
+    LandscapeModuleVue,
+    ModulePropertiesFormVue,
+    ProjectActionsVue,
+    IconVue,
+    LandscapeLoaderVue,
+    LandscapeMiniMapVue,
+  },
   setup() {
     const applicationListener = inject('applicationListener') as ApplicationListener;
     const alertBus = inject('alertBus') as AlertBus;
@@ -146,7 +153,10 @@ export default defineComponent({
         if (!startingElementSlugExists) {
           anchorPointModulesMap.value.set(startingElementSlug, { atStart: true, atEnd: false });
         } else {
-          anchorPointModulesMap.value.set(startingElementSlug, { atStart: true, atEnd: startingElementSlugExists.atEnd });
+          anchorPointModulesMap.value.set(startingElementSlug, {
+            atStart: true,
+            atEnd: startingElementSlugExists.atEnd,
+          });
         }
 
         const endingElementSlugExists = anchorPointModulesMap.value.get(endingElementSlug);
diff --git a/src/main/webapp/app/module/primary/modules-patch/ModulesPatch.component.ts b/src/main/webapp/app/module/primary/modules-patch/ModulesPatch.component.ts
index 5de4a6109..153e75118 100644
--- a/src/main/webapp/app/module/primary/modules-patch/ModulesPatch.component.ts
+++ b/src/main/webapp/app/module/primary/modules-patch/ModulesPatch.component.ts
@@ -1,9 +1,9 @@
-import { AlertBus } from '@/common/domain/alert/AlertBus';
-import { Loader } from '@/loader/primary/Loader';
+import { AlertBus } from '@/shared/alert/domain/AlertBus';
+import { Loader } from '@/shared/loader/infrastructure/primary/Loader';
 import { ModulesRepository } from '@/module/domain/ModulesRepository';
 import { defineComponent, inject, onMounted, reactive, ref } from 'vue';
 import { ProjectFoldersRepository } from '@/module/domain/ProjectFoldersRepository';
-import { IconVue } from '@/common/primary/icon';
+import { IconVue } from '@/shared/icon/infrastructure/primary';
 import { TagFilterVue } from '../tag-filter';
 import { ProjectHistory } from '@/module/domain/ProjectHistory';
 import { ModuleSlug } from '@/module/domain/ModuleSlug';
@@ -21,7 +21,14 @@ import { ModulesPatchLoaderVue } from '../modules-patch-loader';
 
 export default defineComponent({
   name: 'ModulesPatchVue',
-  components: { ModuleParametersVue, IconVue, TagFilterVue, ModulePropertiesFormVue, ProjectActionsVue, ModulesPatchLoaderVue },
+  components: {
+    ModuleParametersVue,
+    IconVue,
+    TagFilterVue,
+    ModulePropertiesFormVue,
+    ProjectActionsVue,
+    ModulesPatchLoaderVue,
+  },
   setup() {
     const alertBus = inject('alertBus') as AlertBus;
     const modules = inject('modules') as ModulesRepository;
diff --git a/src/main/webapp/app/module/primary/project-actions/ProjectActions.component.ts b/src/main/webapp/app/module/primary/project-actions/ProjectActions.component.ts
index 03f802d11..debe4e0e0 100644
--- a/src/main/webapp/app/module/primary/project-actions/ProjectActions.component.ts
+++ b/src/main/webapp/app/module/primary/project-actions/ProjectActions.component.ts
@@ -1,8 +1,8 @@
-import { AlertBus } from '@/common/domain/alert/AlertBus';
+import { AlertBus } from '@/shared/alert/domain/AlertBus';
 import { ModulesRepository } from '@/module/domain/ModulesRepository';
 import { Project } from '@/module/domain/Project';
 import { defineComponent, inject, ref } from 'vue';
-import { IconVue } from '@/common/primary/icon';
+import { IconVue } from '@/shared/icon/infrastructure/primary';
 
 export default defineComponent({
   name: 'ProjectActionsVue',
diff --git a/src/main/webapp/app/module/secondary/RestManagementRepository.ts b/src/main/webapp/app/module/secondary/RestManagementRepository.ts
index 5dcaebf0b..12ae64866 100644
--- a/src/main/webapp/app/module/secondary/RestManagementRepository.ts
+++ b/src/main/webapp/app/module/secondary/RestManagementRepository.ts
@@ -1,4 +1,4 @@
-import { AxiosHttp } from '@/http/AxiosHttp';
+import { AxiosHttp } from '@/shared/http/infrastructure/secondary/AxiosHttp';
 import { ManagementInfo } from '../domain/ManagementInfo';
 import { ManagementRepository } from '@/module/domain/ManagementRepository';
 
diff --git a/src/main/webapp/app/module/secondary/RestModulesRepository.ts b/src/main/webapp/app/module/secondary/RestModulesRepository.ts
index 77419fe22..bf9d0d77c 100644
--- a/src/main/webapp/app/module/secondary/RestModulesRepository.ts
+++ b/src/main/webapp/app/module/secondary/RestModulesRepository.ts
@@ -1,4 +1,4 @@
-import { AxiosHttp } from '@/http/AxiosHttp';
+import { AxiosHttp } from '@/shared/http/infrastructure/secondary/AxiosHttp';
 import { AxiosResponse, RawAxiosRequestConfig } from 'axios';
 import { Modules } from '../domain/Modules';
 import { ModulesRepository } from '../domain/ModulesRepository';
@@ -14,7 +14,7 @@ import { mapToLandscape, RestLandscape } from './RestLandscape';
 import { ProjectHistory } from '../domain/ProjectHistory';
 import { ModulesToApply } from '../domain/ModulesToApply';
 import { RestModulesToApply, toRestModulesToApply } from './RestModulesToApply';
-import { Optional } from '@/common/domain/Optional';
+import { Optional } from '@/shared/optional/domain/Optional';
 
 export class RestModulesRepository implements ModulesRepository {
   constructor(private axiosInstance: AxiosHttp) {}
diff --git a/src/main/webapp/app/module/secondary/RestProjectFoldersRepository.ts b/src/main/webapp/app/module/secondary/RestProjectFoldersRepository.ts
index dc1d9f253..e479d20be 100644
--- a/src/main/webapp/app/module/secondary/RestProjectFoldersRepository.ts
+++ b/src/main/webapp/app/module/secondary/RestProjectFoldersRepository.ts
@@ -1,4 +1,4 @@
-import { AxiosHttp } from '@/http/AxiosHttp';
+import { AxiosHttp } from '@/shared/http/infrastructure/secondary/AxiosHttp';
 import { ProjectFolder } from '../domain/ProjectFolder';
 import { ProjectFoldersRepository } from '@/module/domain/ProjectFoldersRepository';
 
diff --git a/src/main/webapp/app/root/infrastructure/primary/App.component.ts b/src/main/webapp/app/root/infrastructure/primary/App.component.ts
new file mode 100644
index 000000000..e47093a2b
--- /dev/null
+++ b/src/main/webapp/app/root/infrastructure/primary/App.component.ts
@@ -0,0 +1,10 @@
+import { HeaderVue } from '@/shared/header/infrastructure/primary';
+import { ToastVue } from '@/shared/toast/infrastructure/primary';
+
+export default {
+  name: 'App',
+  components: {
+    ToastVue,
+    HeaderVue,
+  },
+};
diff --git a/src/main/webapp/app/common/primary/app/App.html b/src/main/webapp/app/root/infrastructure/primary/App.html
similarity index 100%
rename from src/main/webapp/app/common/primary/app/App.html
rename to src/main/webapp/app/root/infrastructure/primary/App.html
diff --git a/src/main/webapp/app/common/primary/app/App.vue b/src/main/webapp/app/root/infrastructure/primary/App.vue
similarity index 100%
rename from src/main/webapp/app/common/primary/app/App.vue
rename to src/main/webapp/app/root/infrastructure/primary/App.vue
diff --git a/src/main/webapp/app/common/primary/app/index.ts b/src/main/webapp/app/root/infrastructure/primary/index.ts
similarity index 100%
rename from src/main/webapp/app/common/primary/app/index.ts
rename to src/main/webapp/app/root/infrastructure/primary/index.ts
diff --git a/src/main/webapp/app/common/domain/alert/AlertBus.ts b/src/main/webapp/app/shared/alert/domain/AlertBus.ts
similarity index 65%
rename from src/main/webapp/app/common/domain/alert/AlertBus.ts
rename to src/main/webapp/app/shared/alert/domain/AlertBus.ts
index cde43fcb9..4552c9b51 100644
--- a/src/main/webapp/app/common/domain/alert/AlertBus.ts
+++ b/src/main/webapp/app/shared/alert/domain/AlertBus.ts
@@ -1,6 +1,7 @@
-import { AlertMessage } from '@/common/domain/alert/AlertMessage';
+import { AlertMessage } from '@/shared/alert/domain/AlertMessage';
 
 export interface AlertBus {
   success(alertMessage: AlertMessage): void;
+
   error(alertMessage: AlertMessage): void;
 }
diff --git a/src/main/webapp/app/common/domain/alert/AlertListener.ts b/src/main/webapp/app/shared/alert/domain/AlertListener.ts
similarity index 50%
rename from src/main/webapp/app/common/domain/alert/AlertListener.ts
rename to src/main/webapp/app/shared/alert/domain/AlertListener.ts
index 4fa43888e..98db983fc 100644
--- a/src/main/webapp/app/common/domain/alert/AlertListener.ts
+++ b/src/main/webapp/app/shared/alert/domain/AlertListener.ts
@@ -1,7 +1,8 @@
-import { Alerted } from '@/common/domain/alert/Alerted';
-import { Unsubscribe } from '@/common/domain/alert/Unsubscribe';
+import { Alerted } from '@/shared/alert/domain/Alerted';
+import { Unsubscribe } from '@/shared/alert/domain/Unsubscribe';
 
 export interface AlertListener {
   onSuccess(alerted: Alerted): Unsubscribe;
+
   onError(alerted: Alerted): Unsubscribe;
 }
diff --git a/src/main/webapp/app/common/domain/alert/AlertMessage.ts b/src/main/webapp/app/shared/alert/domain/AlertMessage.ts
similarity index 100%
rename from src/main/webapp/app/common/domain/alert/AlertMessage.ts
rename to src/main/webapp/app/shared/alert/domain/AlertMessage.ts
diff --git a/src/main/webapp/app/shared/alert/domain/Alerted.ts b/src/main/webapp/app/shared/alert/domain/Alerted.ts
new file mode 100644
index 000000000..921ab0c6a
--- /dev/null
+++ b/src/main/webapp/app/shared/alert/domain/Alerted.ts
@@ -0,0 +1,3 @@
+import { AlertMessage } from '@/shared/alert/domain/AlertMessage';
+
+export type Alerted = (message: AlertMessage) => void;
diff --git a/src/main/webapp/app/common/domain/alert/Unsubscribe.ts b/src/main/webapp/app/shared/alert/domain/Unsubscribe.ts
similarity index 100%
rename from src/main/webapp/app/common/domain/alert/Unsubscribe.ts
rename to src/main/webapp/app/shared/alert/domain/Unsubscribe.ts
diff --git a/src/main/webapp/app/common/primary/applicationlistener/ApplicationListener.ts b/src/main/webapp/app/shared/alert/infrastructure/primary/ApplicationListener.ts
similarity index 100%
rename from src/main/webapp/app/common/primary/applicationlistener/ApplicationListener.ts
rename to src/main/webapp/app/shared/alert/infrastructure/primary/ApplicationListener.ts
diff --git a/src/main/webapp/app/common/primary/applicationlistener/WindowApplicationListener.ts b/src/main/webapp/app/shared/alert/infrastructure/primary/WindowApplicationListener.ts
similarity index 100%
rename from src/main/webapp/app/common/primary/applicationlistener/WindowApplicationListener.ts
rename to src/main/webapp/app/shared/alert/infrastructure/primary/WindowApplicationListener.ts
diff --git a/src/main/webapp/app/common/secondary/alert/AlertType.ts b/src/main/webapp/app/shared/alert/infrastructure/secondary/AlertType.ts
similarity index 100%
rename from src/main/webapp/app/common/secondary/alert/AlertType.ts
rename to src/main/webapp/app/shared/alert/infrastructure/secondary/AlertType.ts
diff --git a/src/main/webapp/app/common/secondary/alert/MittAlertBus.ts b/src/main/webapp/app/shared/alert/infrastructure/secondary/MittAlertBus.ts
similarity index 61%
rename from src/main/webapp/app/common/secondary/alert/MittAlertBus.ts
rename to src/main/webapp/app/shared/alert/infrastructure/secondary/MittAlertBus.ts
index b113ce1cf..0b2d6f2c5 100644
--- a/src/main/webapp/app/common/secondary/alert/MittAlertBus.ts
+++ b/src/main/webapp/app/shared/alert/infrastructure/secondary/MittAlertBus.ts
@@ -1,7 +1,7 @@
-import { AlertBus } from '@/common/domain/alert/AlertBus';
+import { AlertBus } from '@/shared/alert/domain/AlertBus';
 import { Emitter } from 'mitt';
-import { AlertMessage } from '@/common/domain/alert/AlertMessage';
-import { AlertType } from '@/common/secondary/alert/AlertType';
+import { AlertMessage } from '@/shared/alert/domain/AlertMessage';
+import { AlertType } from '@/shared/alert/infrastructure/secondary/AlertType';
 
 export class MittAlertBus implements AlertBus {
   constructor(private emitter: Emitter<any>) {}
diff --git a/src/main/webapp/app/common/secondary/alert/MittAlertListener.ts b/src/main/webapp/app/shared/alert/infrastructure/secondary/MittAlertListener.ts
similarity index 66%
rename from src/main/webapp/app/common/secondary/alert/MittAlertListener.ts
rename to src/main/webapp/app/shared/alert/infrastructure/secondary/MittAlertListener.ts
index 9c0646a67..a70af7386 100644
--- a/src/main/webapp/app/common/secondary/alert/MittAlertListener.ts
+++ b/src/main/webapp/app/shared/alert/infrastructure/secondary/MittAlertListener.ts
@@ -1,9 +1,9 @@
-import { Alerted } from '@/common/domain/alert/Alerted';
-import { Unsubscribe } from '@/common/domain/alert/Unsubscribe';
+import { Alerted } from '@/shared/alert/domain/Alerted';
+import { Unsubscribe } from '@/shared/alert/domain/Unsubscribe';
 import { Emitter, Handler } from 'mitt';
-import { AlertMessage } from '@/common/domain/alert/AlertMessage';
-import { AlertType } from '@/common/secondary/alert/AlertType';
-import { AlertListener } from '@/common/domain/alert/AlertListener';
+import { AlertMessage } from '@/shared/alert/domain/AlertMessage';
+import { AlertType } from '@/shared/alert/infrastructure/secondary/AlertType';
+import { AlertListener } from '@/shared/alert/domain/AlertListener';
 
 export class MittAlertListener implements AlertListener {
   constructor(private emitter: Emitter<any>) {}
diff --git a/src/main/webapp/app/common/primary/header/Header.component.ts b/src/main/webapp/app/shared/header/infrastructure/primary/Header.component.ts
similarity index 82%
rename from src/main/webapp/app/common/primary/header/Header.component.ts
rename to src/main/webapp/app/shared/header/infrastructure/primary/Header.component.ts
index 8c64cdc59..bbbc08bd9 100644
--- a/src/main/webapp/app/common/primary/header/Header.component.ts
+++ b/src/main/webapp/app/shared/header/infrastructure/primary/Header.component.ts
@@ -1,6 +1,6 @@
 import { defineComponent, inject, ref } from 'vue';
-import { IconVue } from '@/common/primary/icon';
-import { ThemeButtonVue } from '@/common/primary/theme-button';
+import { IconVue } from '@/shared/icon/infrastructure/primary';
+import { ThemeButtonVue } from '@/shared/theme-button/infrastructure/primary';
 import { ManagementRepository } from '@/module/domain/ManagementRepository';
 import { ManagementInfo } from '@/module/domain/ManagementInfo';
 
diff --git a/src/main/webapp/app/common/primary/header/Header.vue b/src/main/webapp/app/shared/header/infrastructure/primary/Header.vue
similarity index 96%
rename from src/main/webapp/app/common/primary/header/Header.vue
rename to src/main/webapp/app/shared/header/infrastructure/primary/Header.vue
index 51fd5357b..b3dd93051 100644
--- a/src/main/webapp/app/common/primary/header/Header.vue
+++ b/src/main/webapp/app/shared/header/infrastructure/primary/Header.vue
@@ -2,7 +2,7 @@
   <header class="jhlite-header">
     <div class="jhlite-header--slot jhlite-with-theme-switch">
       <router-link class="jhlite-logo" to="/">
-        <img class="jhlite-logo--icon" src="../../../../content/JHipster-Lite-neon-blue.png" alt="JHipster bow tie" width="48" />
+        <img class="jhlite-logo--icon" src="../../../../../content/JHipster-Lite-neon-blue.png" alt="JHipster bow tie" width="48" />
         <span class="jhlite-logo--text">JHipster lite</span>
         <span class="jhlite-logo-version--text">{{ version }}</span>
       </router-link>
diff --git a/src/main/webapp/app/common/primary/header/index.ts b/src/main/webapp/app/shared/header/infrastructure/primary/index.ts
similarity index 100%
rename from src/main/webapp/app/common/primary/header/index.ts
rename to src/main/webapp/app/shared/header/infrastructure/primary/index.ts
diff --git a/src/main/webapp/app/http/AxiosHttp.ts b/src/main/webapp/app/shared/http/infrastructure/secondary/AxiosHttp.ts
similarity index 100%
rename from src/main/webapp/app/http/AxiosHttp.ts
rename to src/main/webapp/app/shared/http/infrastructure/secondary/AxiosHttp.ts
diff --git a/src/main/webapp/app/common/primary/icon/Icon.component.ts b/src/main/webapp/app/shared/icon/infrastructure/primary/Icon.component.ts
similarity index 100%
rename from src/main/webapp/app/common/primary/icon/Icon.component.ts
rename to src/main/webapp/app/shared/icon/infrastructure/primary/Icon.component.ts
diff --git a/src/main/webapp/app/common/primary/icon/Icon.vue b/src/main/webapp/app/shared/icon/infrastructure/primary/Icon.vue
similarity index 100%
rename from src/main/webapp/app/common/primary/icon/Icon.vue
rename to src/main/webapp/app/shared/icon/infrastructure/primary/Icon.vue
diff --git a/src/main/webapp/app/common/primary/icon/index.ts b/src/main/webapp/app/shared/icon/infrastructure/primary/index.ts
similarity index 100%
rename from src/main/webapp/app/common/primary/icon/index.ts
rename to src/main/webapp/app/shared/icon/infrastructure/primary/index.ts
diff --git a/src/main/webapp/app/loader/primary/Loader.ts b/src/main/webapp/app/shared/loader/infrastructure/primary/Loader.ts
similarity index 100%
rename from src/main/webapp/app/loader/primary/Loader.ts
rename to src/main/webapp/app/shared/loader/infrastructure/primary/Loader.ts
diff --git a/src/main/webapp/app/common/domain/Logger.ts b/src/main/webapp/app/shared/logger/domain/Logger.ts
similarity index 56%
rename from src/main/webapp/app/common/domain/Logger.ts
rename to src/main/webapp/app/shared/logger/domain/Logger.ts
index d20ea3a03..c0245410a 100644
--- a/src/main/webapp/app/common/domain/Logger.ts
+++ b/src/main/webapp/app/shared/logger/domain/Logger.ts
@@ -1,4 +1,4 @@
-import { Message } from '@/common/domain/Message';
+import { Message } from '@/shared/logger/domain/Message';
 
 export interface Logger {
   error(message: Message, error: Error): void;
diff --git a/src/main/webapp/app/common/domain/Message.ts b/src/main/webapp/app/shared/logger/domain/Message.ts
similarity index 100%
rename from src/main/webapp/app/common/domain/Message.ts
rename to src/main/webapp/app/shared/logger/domain/Message.ts
diff --git a/src/main/webapp/app/common/secondary/ConsoleLogger.ts b/src/main/webapp/app/shared/logger/infrastructure/secondary/ConsoleLogger.ts
similarity index 63%
rename from src/main/webapp/app/common/secondary/ConsoleLogger.ts
rename to src/main/webapp/app/shared/logger/infrastructure/secondary/ConsoleLogger.ts
index f7996b9c5..67ef051a2 100644
--- a/src/main/webapp/app/common/secondary/ConsoleLogger.ts
+++ b/src/main/webapp/app/shared/logger/infrastructure/secondary/ConsoleLogger.ts
@@ -1,5 +1,5 @@
-import { Logger } from '@/common/domain/Logger';
-import { Message } from '@/common/domain/Message';
+import { Logger } from '@/shared/logger/domain/Logger';
+import { Message } from '@/shared/logger/domain/Message';
 
 export default class ConsoleLogger implements Logger {
   constructor(private readonly logger: Console) {}
diff --git a/src/main/webapp/app/common/domain/Memoizer.ts b/src/main/webapp/app/shared/memoizer/domain/Memoizer.ts
similarity index 100%
rename from src/main/webapp/app/common/domain/Memoizer.ts
rename to src/main/webapp/app/shared/memoizer/domain/Memoizer.ts
diff --git a/src/main/webapp/app/common/domain/Optional.ts b/src/main/webapp/app/shared/optional/domain/Optional.ts
similarity index 100%
rename from src/main/webapp/app/common/domain/Optional.ts
rename to src/main/webapp/app/shared/optional/domain/Optional.ts
diff --git a/src/main/webapp/app/common/primary/theme-button/ThemeButton.component.ts b/src/main/webapp/app/shared/theme-button/infrastructure/primary/ThemeButton.component.ts
similarity index 100%
rename from src/main/webapp/app/common/primary/theme-button/ThemeButton.component.ts
rename to src/main/webapp/app/shared/theme-button/infrastructure/primary/ThemeButton.component.ts
diff --git a/src/main/webapp/app/common/primary/theme-button/ThemeButton.vue b/src/main/webapp/app/shared/theme-button/infrastructure/primary/ThemeButton.vue
similarity index 100%
rename from src/main/webapp/app/common/primary/theme-button/ThemeButton.vue
rename to src/main/webapp/app/shared/theme-button/infrastructure/primary/ThemeButton.vue
diff --git a/src/main/webapp/app/common/primary/theme-button/index.ts b/src/main/webapp/app/shared/theme-button/infrastructure/primary/index.ts
similarity index 100%
rename from src/main/webapp/app/common/primary/theme-button/index.ts
rename to src/main/webapp/app/shared/theme-button/infrastructure/primary/index.ts
diff --git a/src/main/webapp/app/common/primary/timeout/Timeout.ts b/src/main/webapp/app/shared/toast/infrastructure/primary/Timeout.ts
similarity index 90%
rename from src/main/webapp/app/common/primary/timeout/Timeout.ts
rename to src/main/webapp/app/shared/toast/infrastructure/primary/Timeout.ts
index 082443c09..652e96ffe 100644
--- a/src/main/webapp/app/common/primary/timeout/Timeout.ts
+++ b/src/main/webapp/app/shared/toast/infrastructure/primary/Timeout.ts
@@ -1,9 +1,10 @@
-import { Optional } from '@/common/domain/Optional';
+import { Optional } from '@/shared/optional/domain/Optional';
 
 export type Timeoutable = () => void;
 
 export interface TimeoutListener {
   register(callable: Timeoutable, milliseconds: number): void;
+
   unregister(): void;
 }
 
@@ -11,6 +12,7 @@ export type TimeoutLauncher = () => TimeoutListener;
 
 export class Timeout implements TimeoutListener {
   private registration: Optional<ReturnType<typeof setTimeout>>;
+
   constructor() {
     this.registration = Optional.empty();
   }
diff --git a/src/main/webapp/app/common/primary/toast/Toast.component.ts b/src/main/webapp/app/shared/toast/infrastructure/primary/Toast.component.ts
similarity index 79%
rename from src/main/webapp/app/common/primary/toast/Toast.component.ts
rename to src/main/webapp/app/shared/toast/infrastructure/primary/Toast.component.ts
index c1d1bb317..9c26c2f54 100644
--- a/src/main/webapp/app/common/primary/toast/Toast.component.ts
+++ b/src/main/webapp/app/shared/toast/infrastructure/primary/Toast.component.ts
@@ -1,11 +1,11 @@
 import { defineComponent, inject, onBeforeUnmount, onMounted, ref, Ref } from 'vue';
-import { Unsubscribe } from '@/common/domain/alert/Unsubscribe';
-import { ToastType } from '@/common/primary/toast/ToastType';
-import { ToastMessage } from '@/common/primary/toast/ToastMessage';
-import { AlertMessage } from '@/common/domain/alert/AlertMessage';
-import { AlertListener } from '@/common/domain/alert/AlertListener';
-import { IconVue } from '@/common/primary/icon';
-import { TimeoutLauncher } from '@/common/primary/timeout/Timeout';
+import { Unsubscribe } from '@/shared/alert/domain/Unsubscribe';
+import { ToastType } from '@/shared/toast/infrastructure/primary/ToastType';
+import { ToastMessage } from '@/shared/toast/infrastructure/primary/ToastMessage';
+import { AlertMessage } from '@/shared/alert/domain/AlertMessage';
+import { AlertListener } from '@/shared/alert/domain/AlertListener';
+import { IconVue } from '@/shared/icon/infrastructure/primary';
+import { TimeoutLauncher } from '@/shared/toast/infrastructure/primary/Timeout';
 
 const WORD_TIME_MS = 300;
 const TOAST_ATTENTION_MS = 1500;


Test patch:
diff --git a/src/main/resources/generator/client/common/cypress/src/test/javascript/integration/cypress-config.ts b/src/main/resources/generator/client/common/cypress/src/test/javascript/integration/cypress-config.ts
index 14a32b687..ed44de359 100644
--- a/src/main/resources/generator/client/common/cypress/src/test/javascript/integration/cypress-config.ts
+++ b/src/main/resources/generator/client/common/cypress/src/test/javascript/integration/cypress-config.ts
@@ -3,7 +3,7 @@ import { defineConfig } from 'cypress';
 export default defineConfig({
   e2e: {
     baseUrl: 'http://localhost:9000',
-    specPattern: 'src/test/javascript/integration/**/*.spec.ts',
+    specPattern: 'src/test/javascript/integration/**/*.(spec|cy).ts',
     fixturesFolder: false,
     supportFile: false,
     video: false,
diff --git a/src/main/resources/generator/client/common/cypress/src/test/javascript/integration/tsconfig.json b/src/main/resources/generator/client/common/cypress/src/test/javascript/integration/tsconfig.json
index 9a307e6cc..3b5ef79df 100644
--- a/src/main/resources/generator/client/common/cypress/src/test/javascript/integration/tsconfig.json
+++ b/src/main/resources/generator/client/common/cypress/src/test/javascript/integration/tsconfig.json
@@ -5,5 +5,5 @@
     "lib": ["es5", "es6", "dom", "dom.iterable"],
     "types": ["cypress"]
   },
-  "include": ["**/*.spec.ts"]
+  "include": ["**/*.ts"]
 }
diff --git a/src/main/resources/generator/client/common/cypress/src/test/javascript/integration/utils/DataSelector.ts b/src/main/resources/generator/client/common/cypress/src/test/javascript/integration/utils/DataSelector.ts
new file mode 100755
index 000000000..e0d3e7c69
--- /dev/null
+++ b/src/main/resources/generator/client/common/cypress/src/test/javascript/integration/utils/DataSelector.ts
@@ -0,0 +1,1 @@
+export const dataSelector = (selector: string): string => `[data-selector="${selector}"]`;
diff --git a/src/main/resources/generator/client/common/cypress/src/test/javascript/integration/utils/Interceptor.ts b/src/main/resources/generator/client/common/cypress/src/test/javascript/integration/utils/Interceptor.ts
new file mode 100755
index 000000000..da9e96637
--- /dev/null
+++ b/src/main/resources/generator/client/common/cypress/src/test/javascript/integration/utils/Interceptor.ts
@@ -0,0 +1,38 @@
+import { HttpResponseInterceptor, RouteMatcher, StaticResponse } from 'cypress/types/net-stubbing';
+
+type ResponseSender = {
+  send: () => void;
+};
+
+const createDeferredPromise = (): [Promise<void>, () => void] => {
+  let resolvePromise: () => void = () => {};
+
+  const promise = new Promise<void>(resolve => {
+    resolvePromise = resolve;
+  });
+
+  return [promise, resolvePromise];
+};
+
+/**
+ * Intercepts a request indefinitely until `send` is called.
+ * @param requestMatcher - The criteria to match the request.
+ * @param response - The response to send when resolved.
+ * @param alias - An optional alias for the intercepted request.
+ * @returns An object with a `send` method to trigger the response.
+ */
+export const interceptForever = (
+  requestMatcher: RouteMatcher,
+  response?: StaticResponse | HttpResponseInterceptor,
+  alias?: string,
+): ResponseSender => {
+  const [deferredPromise, resolveDeferredPromise] = createDeferredPromise();
+
+  cy.intercept(requestMatcher, request =>
+    deferredPromise.then(() => {
+      request.reply(response);
+    }),
+  ).as(alias || 'request');
+
+  return { send: resolveDeferredPromise };
+};
diff --git a/src/main/webapp/app/common/primary/toast/Toast.vue b/src/main/webapp/app/shared/toast/infrastructure/primary/Toast.vue
similarity index 100%
rename from src/main/webapp/app/common/primary/toast/Toast.vue
rename to src/main/webapp/app/shared/toast/infrastructure/primary/Toast.vue
diff --git a/src/main/webapp/app/common/primary/toast/ToastMessage.ts b/src/main/webapp/app/shared/toast/infrastructure/primary/ToastMessage.ts
similarity index 100%
rename from src/main/webapp/app/common/primary/toast/ToastMessage.ts
rename to src/main/webapp/app/shared/toast/infrastructure/primary/ToastMessage.ts
diff --git a/src/main/webapp/app/common/primary/toast/ToastType.ts b/src/main/webapp/app/shared/toast/infrastructure/primary/ToastType.ts
similarity index 100%
rename from src/main/webapp/app/common/primary/toast/ToastType.ts
rename to src/main/webapp/app/shared/toast/infrastructure/primary/ToastType.ts
diff --git a/src/main/webapp/app/common/primary/toast/index.ts b/src/main/webapp/app/shared/toast/infrastructure/primary/index.ts
similarity index 100%
rename from src/main/webapp/app/common/primary/toast/index.ts
rename to src/main/webapp/app/shared/toast/infrastructure/primary/index.ts
diff --git a/src/test/features/client/loader.feature b/src/test/features/client/loader.feature
new file mode 100644
index 000000000..54157f6c8
--- /dev/null
+++ b/src/test/features/client/loader.feature
@@ -0,0 +1,9 @@
+Feature: TS Loader
+
+  Scenario: Should apply TS Loader module
+    When I apply modules to default project
+      | init      |
+      | prettier  |
+      | ts-loader |
+    Then I should have files in "src/main/webapp/app"
+      | shared/loader/infrastructure/primary/Loader.ts |
diff --git a/src/test/features/client/ts-pagination-domain.feature b/src/test/features/client/ts-pagination-domain.feature
new file mode 100644
index 000000000..d56929276
--- /dev/null
+++ b/src/test/features/client/ts-pagination-domain.feature
@@ -0,0 +1,9 @@
+Feature: TS pagination domain
+
+  Scenario: Should apply TS pagination module
+    When I apply modules to default project
+      | init                 |
+      | prettier             |
+      | ts-pagination-domain |
+    Then I should have files in "src/main/webapp/app/shared/pagination/domain"
+      | Page.ts |
diff --git a/src/test/java/tech/jhipster/lite/generator/client/loader/domain/TsLoaderModuleFactoryTest.java b/src/test/java/tech/jhipster/lite/generator/client/loader/domain/TsLoaderModuleFactoryTest.java
new file mode 100644
index 000000000..ca0307eba
--- /dev/null
+++ b/src/test/java/tech/jhipster/lite/generator/client/loader/domain/TsLoaderModuleFactoryTest.java
@@ -0,0 +1,28 @@
+package tech.jhipster.lite.generator.client.loader.domain;
+
+import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.*;
+
+import org.junit.jupiter.api.Test;
+import tech.jhipster.lite.TestFileUtils;
+import tech.jhipster.lite.UnitTest;
+import tech.jhipster.lite.module.domain.JHipsterModule;
+import tech.jhipster.lite.module.domain.JHipsterModulesFixture;
+import tech.jhipster.lite.module.domain.properties.JHipsterModuleProperties;
+
+@UnitTest
+class TsLoaderModuleFactoryTest {
+
+  private static TsLoaderModuleFactory factory = new TsLoaderModuleFactory();
+
+  @Test
+  void shouldBuildModule() {
+    JHipsterModuleProperties properties = JHipsterModulesFixture.propertiesBuilder(TestFileUtils.tmpDirForTest()).build();
+
+    JHipsterModule module = factory.buildModule(properties);
+
+    assertThatModuleWithFiles(module, packageJsonFile())
+      .hasFile("src/main/webapp/app/shared/loader/infrastructure/primary/Loader.ts")
+      .and()
+      .hasFile("src/test/javascript/spec/shared/loader/infrastructure/primary/Loader.spec.ts");
+  }
+}
diff --git a/src/test/java/tech/jhipster/lite/generator/client/pagination_domain/domain/PaginationDomainModuleFactoryTest.java b/src/test/java/tech/jhipster/lite/generator/client/pagination_domain/domain/PaginationDomainModuleFactoryTest.java
new file mode 100644
index 000000000..5ea7ac922
--- /dev/null
+++ b/src/test/java/tech/jhipster/lite/generator/client/pagination_domain/domain/PaginationDomainModuleFactoryTest.java
@@ -0,0 +1,29 @@
+package tech.jhipster.lite.generator.client.pagination_domain.domain;
+
+import static tech.jhipster.lite.module.domain.JHipsterModulesFixture.*;
+import static tech.jhipster.lite.module.infrastructure.secondary.JHipsterModulesAssertions.*;
+
+import org.junit.jupiter.api.Test;
+import tech.jhipster.lite.TestFileUtils;
+import tech.jhipster.lite.UnitTest;
+import tech.jhipster.lite.module.domain.JHipsterModule;
+import tech.jhipster.lite.module.domain.properties.JHipsterModuleProperties;
+
+@UnitTest
+class PaginationDomainModuleFactoryTest {
+
+  private static final TSPaginationDomainModuleFactory factory = new TSPaginationDomainModuleFactory();
+
+  @Test
+  void shouldBuildModule() {
+    JHipsterModuleProperties properties = propertiesBuilder(TestFileUtils.tmpDirForTest()).build();
+
+    JHipsterModule module = factory.buildModule(properties);
+
+    assertThatModuleWithFiles(module, packageJsonFile()).hasPrefixedFiles(
+      "src/main/webapp/app/shared/pagination/domain",
+      "Page.ts",
+      "DisplayedOnPage.ts"
+    );
+  }
+}
diff --git a/src/test/java/tech/jhipster/lite/generator/client/tools/cypress/domain/CypressModuleFactoryTest.java b/src/test/java/tech/jhipster/lite/generator/client/tools/cypress/domain/CypressModuleFactoryTest.java
index 542fd9d6b..3468f4b6b 100644
--- a/src/test/java/tech/jhipster/lite/generator/client/tools/cypress/domain/CypressModuleFactoryTest.java
+++ b/src/test/java/tech/jhipster/lite/generator/client/tools/cypress/domain/CypressModuleFactoryTest.java
@@ -83,6 +83,7 @@ class CypressModuleFactoryTest {
       .containing("baseUrl: 'http://localhost:9000',")
       .and()
       .hasPrefixedFiles("src/test/javascript/integration", ".eslintrc.cjs", "tsconfig.json")
-      .hasFiles("src/test/javascript/integration/common/primary/app/Home.spec.ts");
+      .hasFiles("src/test/javascript/integration/common/primary/app/Home.spec.ts")
+      .hasPrefixedFiles("src/test/javascript/integration/utils", "Interceptor.ts", "DataSelector.ts");
   }
 }
diff --git a/src/test/resources/projects/angular/main.ts b/src/test/resources/projects/angular/main.ts
index d194f646c..66980d6d6 100644
--- a/src/test/resources/projects/angular/main.ts
+++ b/src/test/resources/projects/angular/main.ts
@@ -1,4 +1,4 @@
-import { HTTP_INTERCEPTORS, provideHttpClient, withInterceptors } from '@angular/common/http';
+import { provideHttpClient, withInterceptors } from '@angular/common/http';
 import { enableProdMode, importProvidersFrom } from '@angular/core';
 import { REMOVE_STYLES_ON_COMPONENT_DESTROY, bootstrapApplication } from '@angular/platform-browser';
 import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
diff --git a/src/test/webapp/component/support/Interceptor.ts b/src/test/webapp/component/support/Interceptor.ts
index bf4eae001..da9e96637 100644
--- a/src/test/webapp/component/support/Interceptor.ts
+++ b/src/test/webapp/component/support/Interceptor.ts
@@ -4,18 +4,35 @@ type ResponseSender = {
   send: () => void;
 };
 
-export const interceptForever = (requestMatcher: RouteMatcher, response?: StaticResponse | HttpResponseInterceptor): ResponseSender => {
-  let send;
+const createDeferredPromise = (): [Promise<void>, () => void] => {
+  let resolvePromise: () => void = () => {};
 
-  const trigger = new Promise(resolve => {
-    send = resolve;
+  const promise = new Promise<void>(resolve => {
+    resolvePromise = resolve;
   });
 
-  cy.intercept(requestMatcher, request => {
-    return trigger.then(() => {
+  return [promise, resolvePromise];
+};
+
+/**
+ * Intercepts a request indefinitely until `send` is called.
+ * @param requestMatcher - The criteria to match the request.
+ * @param response - The response to send when resolved.
+ * @param alias - An optional alias for the intercepted request.
+ * @returns An object with a `send` method to trigger the response.
+ */
+export const interceptForever = (
+  requestMatcher: RouteMatcher,
+  response?: StaticResponse | HttpResponseInterceptor,
+  alias?: string,
+): ResponseSender => {
+  const [deferredPromise, resolveDeferredPromise] = createDeferredPromise();
+
+  cy.intercept(requestMatcher, request =>
+    deferredPromise.then(() => {
       request.reply(response);
-    });
-  });
+    }),
+  ).as(alias || 'request');
 
-  return { send };
+  return { send: resolveDeferredPromise };
 };
diff --git a/src/test/webapp/unit/common/primary/cursor/BodyCursorUpdater.spec.ts b/src/test/webapp/unit/common/primary/cursor/BodyCursorUpdater.spec.ts
index ce8862a2b..f3e8816df 100644
--- a/src/test/webapp/unit/common/primary/cursor/BodyCursorUpdater.spec.ts
+++ b/src/test/webapp/unit/common/primary/cursor/BodyCursorUpdater.spec.ts
@@ -1,4 +1,4 @@
-import { BodyCursorUpdater } from '@/common/primary/cursor/BodyCursorUpdater';
+import { BodyCursorUpdater } from '@/module/primary/landscape/BodyCursorUpdater';
 import { stubWindow } from '../../../module/primary/GlobalWindow.fixture';
 import { describe, it, expect } from 'vitest';
 
diff --git a/src/test/webapp/unit/common/primary/header/Header.spec.ts b/src/test/webapp/unit/common/primary/header/Header.spec.ts
index d2df55e71..686b3f69e 100644
--- a/src/test/webapp/unit/common/primary/header/Header.spec.ts
+++ b/src/test/webapp/unit/common/primary/header/Header.spec.ts
@@ -1,4 +1,4 @@
-import { HeaderVue } from '@/common/primary/header';
+import { HeaderVue } from '@/shared/header/infrastructure/primary';
 import { mount, VueWrapper } from '@vue/test-utils';
 import { describe, it, expect } from 'vitest';
 import { ManagementRepositoryStub, stubLocalManagementRepository } from '../../../module/domain/ManagementRepository.fixture';
diff --git a/src/test/webapp/unit/common/primary/icon/Icon.spec.ts b/src/test/webapp/unit/common/primary/icon/Icon.spec.ts
index 9d8118762..b65e2c4ad 100644
--- a/src/test/webapp/unit/common/primary/icon/Icon.spec.ts
+++ b/src/test/webapp/unit/common/primary/icon/Icon.spec.ts
@@ -1,5 +1,5 @@
 import { shallowMount } from '@vue/test-utils';
-import { IconVue } from '@/common/primary/icon';
+import { IconVue } from '@/shared/icon/infrastructure/primary';
 import { describe, it, expect } from 'vitest';
 
 describe('Icon', () => {
diff --git a/src/test/webapp/unit/common/primary/theme-button/ThemeButton.spec.ts b/src/test/webapp/unit/common/primary/theme-button/ThemeButton.spec.ts
index fd50b3b7f..59dbc1cfd 100644
--- a/src/test/webapp/unit/common/primary/theme-button/ThemeButton.spec.ts
+++ b/src/test/webapp/unit/common/primary/theme-button/ThemeButton.spec.ts
@@ -1,8 +1,8 @@
-import { ThemeButtonVue } from '@/common/primary/theme-button/';
 import { VueWrapper, mount } from '@vue/test-utils';
 import { describe, expect, it } from 'vitest';
 import { stubWindow } from '../../../module/primary/GlobalWindow.fixture';
 import { LocalWindowThemeRepositoryStub, stubLocalWindowThemeRepository } from '../../../module/domain/ThemeRepository.fixture';
+import { ThemeButtonVue } from '@/shared/theme-button/infrastructure/primary';
 
 interface WrapperOptions {
   themeRepository: LocalWindowThemeRepositoryStub;
diff --git a/src/test/webapp/unit/common/primary/timeout/Timeout.fixture.ts b/src/test/webapp/unit/common/primary/timeout/Timeout.fixture.ts
index fe96fe58e..85a9beb66 100644
--- a/src/test/webapp/unit/common/primary/timeout/Timeout.fixture.ts
+++ b/src/test/webapp/unit/common/primary/timeout/Timeout.fixture.ts
@@ -1,5 +1,5 @@
 import sinon, { SinonStub } from 'sinon';
-import { Timeoutable, TimeoutListener } from '@/common/primary/timeout/Timeout';
+import { Timeoutable, TimeoutListener } from '@/shared/toast/infrastructure/primary/Timeout';
 
 interface TimeoutListenerStub extends TimeoutListener {
   register: SinonStub<[Timeoutable, number]>;
diff --git a/src/test/webapp/unit/common/primary/timeout/Timeout.spec.ts b/src/test/webapp/unit/common/primary/timeout/Timeout.spec.ts
index 192a6e19a..124bab27d 100644
--- a/src/test/webapp/unit/common/primary/timeout/Timeout.spec.ts
+++ b/src/test/webapp/unit/common/primary/timeout/Timeout.spec.ts
@@ -1,4 +1,4 @@
-import { Timeout } from '@/common/primary/timeout/Timeout';
+import { Timeout } from '@/shared/toast/infrastructure/primary/Timeout';
 import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest';
 
 const TIMEOUT_TIME = 3000;
diff --git a/src/test/webapp/unit/common/primary/toast/Toast.spec.ts b/src/test/webapp/unit/common/primary/toast/Toast.spec.ts
index 1efbadcb3..6524c281d 100644
--- a/src/test/webapp/unit/common/primary/toast/Toast.spec.ts
+++ b/src/test/webapp/unit/common/primary/toast/Toast.spec.ts
@@ -1,9 +1,9 @@
 import { shallowMount, VueWrapper } from '@vue/test-utils';
-import { ToastVue } from '@/common/primary/toast';
-import { ToastType } from '@/common/primary/toast/ToastType';
-import { AlertListener } from '@/common/domain/alert/AlertListener';
-import { AlertListenerFixture, stubAlertListener } from '../../domain/AlertListener.fixure';
-import { TimeoutListener } from '@/common/primary/timeout/Timeout';
+import { ToastVue } from '@/shared/toast/infrastructure/primary';
+import { ToastType } from '@/shared/toast/infrastructure/primary/ToastType';
+import { AlertListener } from '@/shared/alert/domain/AlertListener';
+import { AlertListenerFixture, stubAlertListener } from '../../../shared/alert/domain/AlertListener.fixure';
+import { TimeoutListener } from '@/shared/toast/infrastructure/primary/Timeout';
 import { stubTimeout } from '../timeout/Timeout.fixture';
 import { describe, it, expect, vi } from 'vitest';
 
diff --git a/src/test/webapp/unit/http/AxiosHttp.spec.ts b/src/test/webapp/unit/http/AxiosHttp.spec.ts
index 7df43e716..3276aa635 100644
--- a/src/test/webapp/unit/http/AxiosHttp.spec.ts
+++ b/src/test/webapp/unit/http/AxiosHttp.spec.ts
@@ -1,6 +1,6 @@
 import { describe, it, expect } from 'vitest';
 import { AxiosResponse } from 'axios';
-import { AxiosHttp } from '@/http/AxiosHttp';
+import { AxiosHttp } from '@/shared/http/infrastructure/secondary/AxiosHttp';
 import { dataAxiosResponse, stubAxiosInstance } from './AxiosStub';
 
 interface Payload {
diff --git a/src/test/webapp/unit/http/AxiosHttpStub.ts b/src/test/webapp/unit/http/AxiosHttpStub.ts
index 0e762353f..85035067e 100644
--- a/src/test/webapp/unit/http/AxiosHttpStub.ts
+++ b/src/test/webapp/unit/http/AxiosHttpStub.ts
@@ -1,5 +1,5 @@
 import sinon, { SinonStub } from 'sinon';
-import { AxiosHttp, AxiosHttpResponse } from '@/http/AxiosHttp';
+import { AxiosHttp, AxiosHttpResponse } from '@/shared/http/infrastructure/secondary/AxiosHttp';
 
 export interface AxiosHttpStub extends AxiosHttp {
   get: SinonStub;
diff --git a/src/test/webapp/unit/loader/primary/Loader.spec.ts b/src/test/webapp/unit/loader/primary/Loader.spec.ts
index 01266f5e3..31754c228 100644
--- a/src/test/webapp/unit/loader/primary/Loader.spec.ts
+++ b/src/test/webapp/unit/loader/primary/Loader.spec.ts
@@ -1,4 +1,4 @@
-import { Loader } from '@/loader/primary/Loader';
+import { Loader } from '@/shared/loader/infrastructure/primary/Loader';
 import { describe, it, expect } from 'vitest';
 
 describe('Loader', () => {
diff --git a/src/test/webapp/unit/module/primary/landscape/LandscapeComponent.spec.ts b/src/test/webapp/unit/module/primary/landscape/LandscapeComponent.spec.ts
index 9c7fe302d..179e82a1d 100644
--- a/src/test/webapp/unit/module/primary/landscape/LandscapeComponent.spec.ts
+++ b/src/test/webapp/unit/module/primary/landscape/LandscapeComponent.spec.ts
@@ -1,10 +1,10 @@
-import { ApplicationListener } from '@/common/primary/applicationlistener/ApplicationListener';
+import { ApplicationListener } from '@/shared/alert/infrastructure/primary/ApplicationListener';
 import { ModuleSlug } from '@/module/domain/ModuleSlug';
 import { ModulesRepository } from '@/module/domain/ModulesRepository';
 import { ModulesToApply } from '@/module/domain/ModulesToApply';
 import { LandscapeVue } from '@/module/primary/landscape';
 import { flushPromises, mount, VueWrapper } from '@vue/test-utils';
-import { stubAlertBus } from '../../../common/domain/AlertBus.fixture';
+import { stubAlertBus } from '../../../shared/alert/domain/AlertBus.fixture';
 import { wrappedElement } from '../../../WrappedElement';
 import { defaultLandscape } from '../../domain/landscape/Landscape.fixture';
 import { ModulesRepositoryStub, projectHistoryWithInit, stubModulesRepository } from '../../domain/Modules.fixture';
@@ -12,7 +12,7 @@ import { ProjectFoldersRepositoryStub, stubProjectFoldersRepository } from '../.
 import { ModuleParametersRepositoryStub, stubModuleParametersRepository } from '../../domain/ModuleParameters.fixture';
 import { stubWindow } from '../GlobalWindow.fixture';
 import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
-import { BodyCursorUpdater } from '@/common/primary/cursor/BodyCursorUpdater';
+import { BodyCursorUpdater } from '@/module/primary/landscape/BodyCursorUpdater';
 import { LandscapeScroller } from '@/module/primary/landscape/LandscapeScroller';
 import { ModuleParametersRepository } from '@/module/domain/ModuleParametersRepository';
 
@@ -20,6 +20,7 @@ interface ApplicationListenerStub extends ApplicationListener {
   addEventListener: vi.fn;
   removeEventListener: vi.fn;
 }
+
 interface BodyCursorUpdaterStub extends BodyCursorUpdater {
   set: vi.fn;
   reset: vi.fn;
diff --git a/src/test/webapp/unit/module/primary/modules-patch/ModulesPatchComponent.spec.ts b/src/test/webapp/unit/module/primary/modules-patch/ModulesPatchComponent.spec.ts
index d6a2be51c..71306fcbc 100644
--- a/src/test/webapp/unit/module/primary/modules-patch/ModulesPatchComponent.spec.ts
+++ b/src/test/webapp/unit/module/primary/modules-patch/ModulesPatchComponent.spec.ts
@@ -10,7 +10,7 @@ import {
 import { ModulesVue } from '@/module/primary/modules-patch';
 import { ModulesRepository } from '@/module/domain/ModulesRepository';
 import { wrappedElement } from '../../../WrappedElement';
-import { stubAlertBus } from '../../../common/domain/AlertBus.fixture';
+import { stubAlertBus } from '../../../shared/alert/domain/AlertBus.fixture';
 import { ProjectFoldersRepository } from '@/module/domain/ProjectFoldersRepository';
 import { ProjectFoldersRepositoryStub, stubProjectFoldersRepository } from '../../domain/ProjectFolders.fixture';
 import { ModuleParametersRepositoryStub, stubModuleParametersRepository } from '../../domain/ModuleParameters.fixture';
diff --git a/src/test/webapp/unit/module/primary/project-actions/ProjectActionsComponent.spec.ts b/src/test/webapp/unit/module/primary/project-actions/ProjectActionsComponent.spec.ts
index 7447a4b6c..59f446926 100644
--- a/src/test/webapp/unit/module/primary/project-actions/ProjectActionsComponent.spec.ts
+++ b/src/test/webapp/unit/module/primary/project-actions/ProjectActionsComponent.spec.ts
@@ -1,6 +1,6 @@
 import { ProjectActionsVue } from '@/module/primary/project-actions';
 import { flushPromises, shallowMount, VueWrapper } from '@vue/test-utils';
-import { stubAlertBus } from '../../../common/domain/AlertBus.fixture';
+import { stubAlertBus } from '../../../shared/alert/domain/AlertBus.fixture';
 import { defaultProject, stubModulesRepository } from '../../domain/Modules.fixture';
 import { ModulesRepository } from '@/module/domain/ModulesRepository';
 import { wrappedElement } from '../../../WrappedElement';
diff --git a/src/test/webapp/unit/common/primary/app/App.spec.ts b/src/test/webapp/unit/root/infrastructure/primary/App.spec.ts
similarity index 86%
rename from src/test/webapp/unit/common/primary/app/App.spec.ts
rename to src/test/webapp/unit/root/infrastructure/primary/App.spec.ts
index 34e2b5026..c7f7ba7fd 100644
--- a/src/test/webapp/unit/common/primary/app/App.spec.ts
+++ b/src/test/webapp/unit/root/infrastructure/primary/App.spec.ts
@@ -1,5 +1,5 @@
 import { shallowMount, VueWrapper } from '@vue/test-utils';
-import { AppVue } from '@/common/primary/app';
+import { AppVue } from '@/root/infrastructure/primary';
 import { describe, it, expect } from 'vitest';
 
 let wrapper: VueWrapper;
diff --git a/src/test/webapp/unit/common/domain/AlertBus.fixture.ts b/src/test/webapp/unit/shared/alert/domain/AlertBus.fixture.ts
similarity index 80%
rename from src/test/webapp/unit/common/domain/AlertBus.fixture.ts
rename to src/test/webapp/unit/shared/alert/domain/AlertBus.fixture.ts
index 6fff006d7..0707f3fbd 100644
--- a/src/test/webapp/unit/common/domain/AlertBus.fixture.ts
+++ b/src/test/webapp/unit/shared/alert/domain/AlertBus.fixture.ts
@@ -1,5 +1,5 @@
 import sinon, { SinonStub } from 'sinon';
-import { AlertBus } from '@/common/domain/alert/AlertBus';
+import { AlertBus } from '@/shared/alert/domain/AlertBus';
 
 export interface AlertBusFixture extends AlertBus {
   success: SinonStub;
diff --git a/src/test/webapp/unit/common/domain/AlertListener.fixure.ts b/src/test/webapp/unit/shared/alert/domain/AlertListener.fixure.ts
similarity index 81%
rename from src/test/webapp/unit/common/domain/AlertListener.fixure.ts
rename to src/test/webapp/unit/shared/alert/domain/AlertListener.fixure.ts
index 754bc7628..6a9a97c64 100644
--- a/src/test/webapp/unit/common/domain/AlertListener.fixure.ts
+++ b/src/test/webapp/unit/shared/alert/domain/AlertListener.fixure.ts
@@ -1,5 +1,5 @@
 import sinon, { SinonStub } from 'sinon';
-import { AlertListener } from '@/common/domain/alert/AlertListener';
+import { AlertListener } from '@/shared/alert/domain/AlertListener';
 
 export interface AlertListenerFixture extends AlertListener {
   onSuccess: SinonStub;
diff --git a/src/test/webapp/unit/common/secondary/EmitterStub.fixture.ts b/src/test/webapp/unit/shared/alert/infrastructure/secondary/EmitterStub.fixture.ts
similarity index 100%
rename from src/test/webapp/unit/common/secondary/EmitterStub.fixture.ts
rename to src/test/webapp/unit/shared/alert/infrastructure/secondary/EmitterStub.fixture.ts
diff --git a/src/test/webapp/unit/common/secondary/alert/MittAlertBus.spec.ts b/src/test/webapp/unit/shared/alert/infrastructure/secondary/MittAlertBus.spec.ts
similarity index 84%
rename from src/test/webapp/unit/common/secondary/alert/MittAlertBus.spec.ts
rename to src/test/webapp/unit/shared/alert/infrastructure/secondary/MittAlertBus.spec.ts
index b08bee5e6..6fca1c971 100644
--- a/src/test/webapp/unit/common/secondary/alert/MittAlertBus.spec.ts
+++ b/src/test/webapp/unit/shared/alert/infrastructure/secondary/MittAlertBus.spec.ts
@@ -1,6 +1,6 @@
 import { Emitter } from 'mitt';
-import { MittAlertBus } from '@/common/secondary/alert/MittAlertBus';
-import { AlertType } from '@/common/secondary/alert/AlertType';
+import { MittAlertBus } from '@/shared/alert/infrastructure/secondary/MittAlertBus';
+import { AlertType } from '@/shared/alert/infrastructure/secondary/AlertType';
 import { describe, expect, it, vi } from 'vitest';
 
 interface EmitterStub extends Emitter<any> {
diff --git a/src/test/webapp/unit/common/secondary/alert/MittAlertListener.spec.ts b/src/test/webapp/unit/shared/alert/infrastructure/secondary/MittAlertListener.spec.ts
similarity index 91%
rename from src/test/webapp/unit/common/secondary/alert/MittAlertListener.spec.ts
rename to src/test/webapp/unit/shared/alert/infrastructure/secondary/MittAlertListener.spec.ts
index 51fe9e05a..ffcf3eab2 100644
--- a/src/test/webapp/unit/common/secondary/alert/MittAlertListener.spec.ts
+++ b/src/test/webapp/unit/shared/alert/infrastructure/secondary/MittAlertListener.spec.ts
@@ -1,6 +1,6 @@
 import mitt from 'mitt';
-import { MittAlertListener } from '@/common/secondary/alert/MittAlertListener';
-import { AlertType } from '@/common/secondary/alert/AlertType';
+import { MittAlertListener } from '@/shared/alert/infrastructure/secondary/MittAlertListener';
+import { AlertType } from '@/shared/alert/infrastructure/secondary/AlertType';
 import { describe, it, expect, vi } from 'vitest';
 
 describe('MittAlertListener', () => {
diff --git a/src/test/webapp/unit/common/domain/Logger.fixture.ts b/src/test/webapp/unit/shared/logger/domain/Logger.fixture.ts
similarity index 77%
rename from src/test/webapp/unit/common/domain/Logger.fixture.ts
rename to src/test/webapp/unit/shared/logger/domain/Logger.fixture.ts
index bc36fd9a5..adb45d23f 100644
--- a/src/test/webapp/unit/common/domain/Logger.fixture.ts
+++ b/src/test/webapp/unit/shared/logger/domain/Logger.fixture.ts
@@ -1,5 +1,5 @@
 import sinon, { SinonStub } from 'sinon';
-import { Logger } from '@/common/domain/Logger';
+import { Logger } from '@/shared/logger/domain/Logger';
 
 export interface LoggerFixture extends Logger {
   error: SinonStub;
diff --git a/src/test/webapp/unit/common/secondary/ConsoleLogger.spec.ts b/src/test/webapp/unit/shared/logger/infrastructure/secondary/ConsoleLogger.spec.ts
similarity index 84%
rename from src/test/webapp/unit/common/secondary/ConsoleLogger.spec.ts
rename to src/test/webapp/unit/shared/logger/infrastructure/secondary/ConsoleLogger.spec.ts
index bd99e1a0b..6b8953995 100644
--- a/src/test/webapp/unit/common/secondary/ConsoleLogger.spec.ts
+++ b/src/test/webapp/unit/shared/logger/infrastructure/secondary/ConsoleLogger.spec.ts
@@ -1,4 +1,4 @@
-import ConsoleLogger from '@/common/secondary/ConsoleLogger';
+import ConsoleLogger from '@/shared/logger/infrastructure/secondary/ConsoleLogger';
 import { describe, it, expect, vi } from 'vitest';
 
 describe('ConsoleLogger', () => {
diff --git a/src/test/webapp/unit/common/domain/Optional.spec.ts b/src/test/webapp/unit/shared/optional/domain/Optional.spec.ts
similarity index 98%
rename from src/test/webapp/unit/common/domain/Optional.spec.ts
rename to src/test/webapp/unit/shared/optional/domain/Optional.spec.ts
index 17214796c..a185044cb 100644
--- a/src/test/webapp/unit/common/domain/Optional.spec.ts
+++ b/src/test/webapp/unit/shared/optional/domain/Optional.spec.ts
@@ -1,4 +1,4 @@
-import { Optional } from '@/common/domain/Optional';
+import { Optional } from '@/shared/optional/domain/Optional';
 import { describe, expect, it } from 'vitest';
 
 describe('Optional', () => {
diff --git a/tests-ci/generate.sh b/tests-ci/generate.sh
index 6c1f3973d..f6228f34f 100755
--- a/tests-ci/generate.sh
+++ b/tests-ci/generate.sh
@@ -133,7 +133,9 @@ elif [[ $application == 'fullapp' ]]; then
   sonar_back_front
 
   applyModules \
+  "ts-pagination-domain" \
   "prettier" \
+  "ts-loader" \
   "infinitest-filters" \
   "pagination-domain" \
   "rest-pagination" \