Skip to main content

TeaQL Best Practices in Spring Boot / Spring Cloud

This document summarizes recommended usage patterns and best practices for TeaQL in Spring Boot / Spring Cloud projects. It is intended to help teams build clean layering, strong testability, and long-term maintainability.


In Spring Boot / Spring Cloud projects, the recommended invocation chain is:

Controller -> Service -> Util

Design Goals​

  • Controller: protocol adaptation only (HTTP / RPC)
  • Service: transaction boundaries and business orchestration
  • Util: reusable, testable, pure business logic

2. Unified Context: CustomUserContext​

CustomUserContext is a project-specific context class.

Why it matters​

  • Centralizes user, permission, tenant, and request metadata
  • Enables shared common methods across layers
  • Prevents excessive parameter passing

3. Controller Layer Practices​

Example​

@GetMapping("/info")
public Object info(@TQLContext CustomUserContext userContext) {
return service.info(userContext);
}

Best Practices​

  • The first parameter in Controller methods should be annotated with @TQLContext
  • Keep Controllers minimal:
    • No business logic
    • No complex condition handling
    • No transaction management
  • Controllers should only:
    • Accept request parameters
    • Delegate to Service
    • Return results

4. Service Layer Practices​

Example​

public Object info(@TQLContext CustomUserContext userContext, OtherParam param) {
return InfoUtil.info(userContext, param);
}

Best Practices​

  • Service layer responsibilities:
    • Define transaction boundaries (e.g. @Transactional)
    • Orchestrate business flows
  • Inside Service methods:
    • Keep logic concise
    • Avoid complex TeaQL expressions
    • Delegate heavy logic to Util layer

5. Util Layer Practices​

Example​

public class InfoUtil {

public static Object info(@TQLContext CustomUserContext userContext, OtherParam param) {
return Q.orders().withUserId(userContext.getUserId())
.comment("Query orders").purpose("Load data")
.executeForList(userContext);
}
}

Best Practices​

  • Prefer static methods
  • Decouple from Spring Beans and lifecycle
  • Key benefits:
    • Easier unit testing
    • Better reusability
    • Clearer composition across Services

6. General TeaQL Best Practices​

6.1 Ensure Schema at Setup or Admin Boundary​

Java 1.525-RELEASE database providers expose schema work through the SchemaExecutor capability. Application wiring must retain the configured provider executor and call ensureSchema(UserContext) only from a controlled setup or admin boundary. Do not assume the provider executor is automatically available as a Spring bean unless the selected integration module documents that contract.

The current modular source does not bind the legacy teaql.ensureSchema property. If an older Spring starter supplies such a key, treat it as starter-version-specific and verify its configuration metadata and default.

Best practices:

  • Run schema ensure during setup, deployment, startup, or controlled admin operations.
  • Keep schema ensure out of domain objects and business methods.
  • Use normal generated Q queries and save(userContext) only after the schema is ready.
  • Keep environment-specific database credentials and provider configuration in application configuration, not in model or domain behavior code.

6.2 Minimize RawSQL Usage​

  • Avoid using RawSQL whenever possible
  • Prefer:
    • Semantic TeaQL expressions
    • Composable query fragments

6.3 Avoid DTO Explosion​

  • Do not create large numbers of scenario-specific DTOs
  • Recommended approach:
    • Extend generated Entity Classes
    • Add business logic in subclasses

6.4 Entity Classes as DDD Aggregate Roots​

  • Extend Entity Classes
  • Add lightweight business behaviors directly to entities
  • Use them as Aggregate Roots in DDD design
  • Keep domain methods pure enough to unit test without Spring, database, or UserContext
  • Do not hide persistence calls such as save(userContext) inside domain methods

6.5 Keep Domain Behavior Lightweight​

TeaQL supports DDD-style return types through returnType(Class). This lets a query return a subclass with additional domain behavior:

class OperableMerchant extends Merchant {

OperableMerchant openStore() {
updateStatus(MerchantStatus.ACTIVE);
return this;
}
}

var merchant = Q.merchants().returnType(OperableMerchant.class)
.filterByName("TeaQL Store")
.comment("Query merchants").purpose("Load data")
.executeForOne(userContext)
.orElseThrow();

merchant.openStore();
merchant.auditAs("Save merchant").save(userContext);

The important boundary is intentional:

  • Domain methods express business state changes and validations.
  • Service or application methods own transactions, orchestration, and persistence.
  • This keeps aggregate behavior easy to unit test.
  • This prevents domain objects from depending on runtime infrastructure.

Avoid this pattern:

class OperableMerchant extends Merchant {

void openStore(UserContext userContext) {
updateStatus(MerchantStatus.ACTIVE);
save(userContext);
}
}

The second version couples domain behavior to persistence and makes tests more expensive. Keep the object light and let the service decide when to save.


6.6 Dynamic Properties​

TeaQL supports dynamic properties:

bean.setDynamicProperty("name", value);
  • After JSON serialization, the field appears as:
    • _name
  • Suitable for:
    • Temporary frontend fields
    • Non-persistent computed values

6.7 Query Fragment Reuse​

TeaQL allows reusable query fragments via static methods:

public static OrderRequest shippedOrders() {
return Q.orders().withStatusAreShipped();
}

Further composition:

shippedOrders().filterByUserId(userId);

Benefits:

  • Unified business semantics
  • Reduced duplication
  • Improved readability

6.8 Readability in Complex Scenarios​

  • TeaQL supports deeply nested queries
  • For complex business scenarios:
    • Use semantic Request wrappers
    • Let method names express business intent

This significantly improves:

  • Code readability
  • Maintainability in complex domains

7. Summary​

By following these practices:

  • TeaQL queries remain clear and centralized
  • Spring Boot / Cloud layering stays stable
  • Code becomes easier to test and evolve

Recommended for:

  • Medium to large Spring Cloud projects
  • Systems where TeaQL is the core data access and business expression layer