Validated at Compile Time
APT catches violations before your app runs. Missing @QfCrypto? Skip a privilege declaration? Build fails. Not a warning — an error.
Define intent once. APT enforces it at compile time. Runtime enforces it everywhere else. Less code is a side effect — correctness is the goal.
@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 errorNo extra service. No extra config. The annotation is the contract — the system enforces it.
@QfEntity(capabilityKey = "employee-management")
class Employee {
@QfCrypto
String salary;
}Annotations are the machine-readable spec. Not documentation. Not convention. A contract.
javac + q-apt (Annotation Processor)
→ reads @QfEntity, @QfCrypto, @QfCapability ...
→ validates constraint combinations
→ generates metadata (resultcodes.json, entity metadata)
→ if violation found → BUILD FAILSNothing invalid reaches your running application.
Request → Q Runtime
→ checks Capability / Privilege
→ applies organization scope filter
→ applies @QfCrypto encrypt/decrypt
→ enforces validation rules
→ returns structured QfResultCode on failureYou don't write this. You declared it. Q runs it.
| Question | Answer |
|---|---|
| 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. |
Constraint violated at compile time:
error: [Q-Framework] @QfCrypto cannot be combined with @QfSearch on the same field.
→ Employee.salaryBuild stops. Nothing ships.
Constraint violated at runtime:
{
"code": "Q-AUTH-000001",
"cause": "Required privilege not present.",
"resolution": "Assign the required privilege to the caller's role."
}Structured. Consistent. Every time.
❌ Standard Spring — 1 entity, 7 files, ~400 lines
// 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 rulesLess 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.
@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.
The biggest fear: "What if I can't control it?"
You can override everything.
// 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 keep | How |
|---|---|
| Your query logic | QfEntityPersistenceAdapter |
| Your pre/post hooks | @QfOn on entity methods |
| Your auth provider | QfUserProvider SPI |
| Your org hierarchy | QfOrganizationProvider SPI |
| Your existing stack | Spring Boot, JPA, your DB — unchanged |
Q handles the 90% that's the same everywhere.You own the 10% that makes your product different.
Backend — add to your Spring Boot project:
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'
}<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:
pnpm add @softminds/q-nuxtnpm install @softminds/q-nuxt// nuxt.config.ts
export default defineNuxtConfig({
extends: ['@softminds/q-nuxt'],
})Annotate an entity. Start the app. APIs and UI are live.