Skip to content

Don't rely on AI.Make correctness a system property.

Define intent once. APT enforces it at compile time. Runtime enforces it everywhere else. Less code is a side effect — correctness is the goal.

Q-Framework

One declaration. Look at what the system does.

java
@QfEntity
class Employee {

    @QfCrypto
    String salary;

    @QfCrypto
    String phone;
}
DB:  salary, phone  → stored encrypted automatically
API: salary, phone  → decrypted automatically on read
UI:  salary, phone  → shown only to authorized roles
If @QfCrypto is missing → compile error

No extra service. No extra config. The annotation is the contract — the system enforces it.


How it works

1 — You declare intent

java
@QfEntity(capabilityKey = "employee-management")
class Employee {
    @QfCrypto
    String salary;
}

Annotations are the machine-readable spec. Not documentation. Not convention. A contract.

2 — APT validates and generates at compile time

javac + q-apt (Annotation Processor)
  → reads @QfEntity, @QfCrypto, @QfCapability ...
  → validates constraint combinations
  → generates metadata (resultcodes.json, entity metadata)
  → if violation found → BUILD FAILS

Nothing invalid reaches your running application.

3 — Runtime enforces the contract

Request → Q Runtime
  → checks Capability / Privilege
  → applies organization scope filter
  → applies @QfCrypto encrypt/decrypt
  → enforces validation rules
  → returns structured QfResultCode on failure

You don't write this. You declared it. Q runs it.


What the system guarantees

QuestionAnswer
When is this validated?Compile time (APT) + Runtime (fail-fast)
What happens on failure?Build error — or structured QfResultCode response, always
Can I override anything?Yes. SPI adapters, @QfOn hooks, custom validators — all pure Java
Performance overhead?Metadata generated at compile time. No reflection. No startup scan.
Works with existing code?Yes. access.mode=none disables checks. Add constraints incrementally.

Failure model

Constraint violated at compile time:

error: [Q-Framework] @QfCrypto cannot be combined with @QfSearch on the same field.
  → Employee.salary

Build stops. Nothing ships.


Constraint violated at runtime:

json
{
  "code": "Q-AUTH-000001",
  "cause": "Required privilege not present.",
  "resolution": "Assign the required privilege to the caller's role."
}

Structured. Consistent. Every time.


Before vs After

❌ Standard Spring — 1 entity, 7 files, ~400 lines

java
// UserEntity.java
@Entity @Table(name = "users")
public class UserEntity {
    @Id @GeneratedValue private Long id;
    @Column(nullable = false) private String name;
    @Column private String email;    // encrypted? your problem
    @Column private String orgId;
}

// UserDto.java          — duplicate fields
// UserMapper.java       — boilerplate mapping
// UserRepository.java   — JPA boilerplate
// UserService.java      — validation, encryption, org filter
// UserController.java   — REST endpoints
// SecurityConfig.java   — access control rules

Less than 10% is real business logic. AI writes this differently every time. The security review is on you.

✅ Q-Framework — 1 file. Correct by construction.

java
@QfEntity(
  appKey = "app",
  organizationPolicy = @QfOrganizationPolicy(
    enabled = true, attribute = "organizationId"),
  capabilityKey = "user-management"
)
public class UserEntity {

  private String organizationId;

  @QfListAttribute @QfDetailAttribute
  @QfCreateAttribute(requiredOn = @QfRequiredOn(always = true))
  @QfDisplayLabel(text = "Name")
  private String name;

  @QfDetailAttribute @QfCreateAttribute
  @QfCrypto
  @QfControlType(QfControlType.Type.email)
  @QfDisplayLabel(text = "Email")
  private String email;
}

We don't hide code. We eliminate meaningless labor.


What you don't lose

The biggest fear: "What if I can't control it?"

You can override everything.

java
// Replace the default list query for Employee — plug in your own
@Component
public class EmployeeSearchAdapter implements QfEntityPersistenceAdapter {
    @Override
    public QfEntityListResponse list(QfEntityMetadataDoc.Entity meta,
                                     QfEntityListRequest req) {
        // Your QueryDSL, your predicates, your rules.
        // Q calls this instead of the default implementation.
        return queryFactory.select(...)
            .where(buildComplexPredicate(req))
            .fetch();
    }
}
What you keepHow
Your query logicQfEntityPersistenceAdapter
Your pre/post hooks@QfOn on entity methods
Your auth providerQfUserProvider SPI
Your org hierarchyQfOrganizationProvider SPI
Your existing stackSpring Boot, JPA, your DB — unchanged

Q handles the 90% that's the same everywhere.You own the 10% that makes your product different.


Quick Start

Backend — add to your Spring Boot project:

groovy
dependencies {
  implementation platform('net.softminds.qframework:q-spring-boot-bom:1.0.0-SNAPSHOT')
  implementation 'net.softminds.qframework:q-spring-boot-starter:1.0.0-SNAPSHOT'
  implementation 'net.softminds.qframework:q-spring-boot-jpa:1.0.0-SNAPSHOT'
  annotationProcessor 'net.softminds.qframework:q-apt:1.0.0-SNAPSHOT'
}
xml
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>net.softminds.qframework</groupId>
      <artifactId>q-spring-boot-bom</artifactId>
      <version>1.0.0-SNAPSHOT</version>
      <type>pom</type><scope>import</scope>
    </dependency>
  </dependencies>
</dependencyManagement>
<dependencies>
  <dependency>
    <groupId>net.softminds.qframework</groupId>
    <artifactId>q-spring-boot-starter</artifactId>
  </dependency>
  <dependency>
    <groupId>net.softminds.qframework</groupId>
    <artifactId>q-spring-boot-jpa</artifactId>
  </dependency>
</dependencies>

Frontend — add to your Nuxt 3 project:

bash
pnpm add @softminds/q-nuxt
bash
npm install @softminds/q-nuxt
ts
// nuxt.config.ts
export default defineNuxtConfig({
  extends: ['@softminds/q-nuxt'],
})

Annotate an entity. Start the app. APIs and UI are live.

Full Quick Start Guide

Released under the Apache 2.0 License.