Merged PR 2430: Add API grouping facade rollout checkpoint
Add API grouping facade rollout checkpoint Related work items: #23754
This commit is contained in:
@@ -259,6 +259,65 @@ It means:
|
|||||||
- excluded adjacent search journeys remain unchanged
|
- excluded adjacent search journeys remain unchanged
|
||||||
- no route migration, deletion, or contract change has occurred
|
- no route migration, deletion, or contract change has occurred
|
||||||
|
|
||||||
|
#### API Grouping Façade Rollout Checkpoint
|
||||||
|
|
||||||
|
- The first successful façade slices now establish a proven additive rollout pattern for:
|
||||||
|
- subscriptions / watched cases
|
||||||
|
- documents / published document metadata
|
||||||
|
- public search results
|
||||||
|
- Proven runtime shape:
|
||||||
|
|
||||||
|
```text
|
||||||
|
UI journey
|
||||||
|
→ existing service layer
|
||||||
|
→ grouped façade route
|
||||||
|
→ legacy canonical handler
|
||||||
|
→ unchanged integration behaviour
|
||||||
|
```
|
||||||
|
|
||||||
|
- Proven implementation pattern:
|
||||||
|
- audit the active journey path first
|
||||||
|
- identify the existing owning/common service layer already used by that path
|
||||||
|
- add grouped façade routes only for the proven in-scope active calls
|
||||||
|
- implement each grouped route as a tiny wrapper that delegates directly to the existing legacy handler
|
||||||
|
- keep the legacy handler as the canonical implementation
|
||||||
|
- adopt the grouped route in the existing service layer only where the active journey path is proven and low-risk
|
||||||
|
- leave adjacent, inactive, support, or orchestration-heavy routes out of scope unless separately justified
|
||||||
|
- prove the slice with lightweight characterization tests covering delegation, service adoption, retained legacy handlers, and unchanged scope boundaries
|
||||||
|
- Important interpretation:
|
||||||
|
- this checkpoint proves a safe rollout pattern for additive façade grouping
|
||||||
|
- it does not approve route removal
|
||||||
|
- it does not approve handler replacement
|
||||||
|
- it does not approve broad migration
|
||||||
|
- it does not approve grouping of more complex API families without another bounded audit
|
||||||
|
|
||||||
|
#### Vertical façade slice completion criteria
|
||||||
|
|
||||||
|
- A vertical façade slice should be treated as complete only when:
|
||||||
|
- active journey paths are audited
|
||||||
|
- grouped façade routes exist for the in-scope active routes only
|
||||||
|
- the service layer adopts the grouped routes where safe and proven
|
||||||
|
- legacy handlers remain present and canonical behind the façade
|
||||||
|
- inactive or support routes are explicitly left out of scope where they are not part of the active adopted path
|
||||||
|
- characterization tests prove façade delegation and service-layer adoption
|
||||||
|
- characterization tests do not imply that legacy handlers can be removed
|
||||||
|
- documentation records the exact scope, completion meaning, exclusions, and non-goals
|
||||||
|
- A slice is not complete merely because grouped files exist.
|
||||||
|
- Completion requires both bounded adoption evidence and explicit documentation of what remains outside scope.
|
||||||
|
|
||||||
|
#### Do not group yet criteria
|
||||||
|
|
||||||
|
- Grouping should be delayed when any of the following apply:
|
||||||
|
- no clear service layer exists
|
||||||
|
- active ownership is unclear
|
||||||
|
- the route is orchestration-heavy
|
||||||
|
- the route crosses storage + queue + CRM boundaries
|
||||||
|
- the route is auth/session critical
|
||||||
|
- the route is not proven active in a bounded current journey
|
||||||
|
- grouping would require frontend refactor rather than additive service-level adoption
|
||||||
|
- grouping would require contract change
|
||||||
|
- In these cases, documentation may still classify the area conceptually, but additive façade rollout should wait for a safer bounded slice.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Stage 3 — Documentation Maturity
|
### Stage 3 — Documentation Maturity
|
||||||
|
|||||||
@@ -20,6 +20,73 @@ Follow-ups:
|
|||||||
|
|
||||||
### CL-2026-06-25-API-GROUPING-FACADE-SEARCH-VERTICAL-SLICE: active public search results grouping and service adoption
|
### CL-2026-06-25-API-GROUPING-FACADE-SEARCH-VERTICAL-SLICE: active public search results grouping and service adoption
|
||||||
|
|
||||||
|
### CL-2026-06-25-API-GROUPING-FACADE-ROLLOUT-CHECKPOINT: proven rollout pattern and guardrails checkpoint
|
||||||
|
|
||||||
|
date: 2026-06-25
|
||||||
|
author: Cline
|
||||||
|
scope: `context/api-grouping-adoption-roadmap.md`, `tests/phase22/api-grouping-facade-rollout.test.cjs`, `memory-bank/change-log.md`
|
||||||
|
type: milestone
|
||||||
|
rationale: Record a short checkpoint after the first successful additive API grouping façade slices so the proven rollout pattern, completion criteria, and delay/guardrail criteria are documented before considering more complex API groups.
|
||||||
|
impact: Documentation and characterization only; no runtime behaviour, handler implementation, service URL, route location, payload, contract, auth/session behaviour, or integration behaviour change.
|
||||||
|
status: completed
|
||||||
|
|
||||||
|
Summary:
|
||||||
|
|
||||||
|
- Confirmed the required checkpoint context was read before implementation:
|
||||||
|
- `context/api-grouping-plan.md`
|
||||||
|
- `context/api-grouping-adoption-roadmap.md`
|
||||||
|
- `context/api-route-map.md`
|
||||||
|
- `context/portal-api-platform-assessment.md`
|
||||||
|
- `context/architecture.md`
|
||||||
|
- `memory-bank/change-log.md`
|
||||||
|
- existing façade tests under `tests/phase22`
|
||||||
|
- Confirmed the proven slice pattern now established by the completed façade areas:
|
||||||
|
- `pages/api/subscriptions/*`
|
||||||
|
- `pages/api/documents/*`
|
||||||
|
- `pages/api/search/*`
|
||||||
|
- Recorded the rollout checkpoint in the adoption roadmap with the proven shape:
|
||||||
|
- UI journey
|
||||||
|
- existing service layer
|
||||||
|
- grouped façade route
|
||||||
|
- legacy canonical handler
|
||||||
|
- unchanged integration behaviour
|
||||||
|
- Documented completion criteria for a vertical façade slice, including:
|
||||||
|
- bounded active-journey audit
|
||||||
|
- grouped façade coverage for in-scope active routes only
|
||||||
|
- safe service-layer adoption
|
||||||
|
- retained canonical legacy handlers
|
||||||
|
- explicit out-of-scope treatment for inactive/support routes
|
||||||
|
- characterization proof and scope documentation
|
||||||
|
- Documented “do not group yet” criteria for cases such as:
|
||||||
|
- unclear ownership
|
||||||
|
- missing clear service layer
|
||||||
|
- orchestration-heavy routes
|
||||||
|
- storage + queue + CRM crossing routes
|
||||||
|
- auth/session-critical routes
|
||||||
|
- unproven activity
|
||||||
|
- required frontend refactor
|
||||||
|
- required contract change
|
||||||
|
- Added a lightweight meta-test to characterize the checkpoint structure without introducing a larger framework.
|
||||||
|
|
||||||
|
Validation:
|
||||||
|
|
||||||
|
- Focused validation only for this checkpoint:
|
||||||
|
- `npx eslint tests/phase22/api-grouping-facade-rollout.test.cjs`
|
||||||
|
- `node tests/phase22/api-grouping-facade-rollout.test.cjs`
|
||||||
|
- `node tests/phase22/api-grouping-facade-subscriptions.test.cjs`
|
||||||
|
- `node tests/phase22/api-grouping-facade-documents.test.cjs`
|
||||||
|
- `node tests/phase22/api-grouping-facade-search.test.cjs`
|
||||||
|
- No heavy repo-wide tooling run.
|
||||||
|
|
||||||
|
Follow-ups:
|
||||||
|
|
||||||
|
- Use this checkpoint as the baseline before considering any further façade candidates.
|
||||||
|
- Prefer the next candidate only where the same additive pattern can be proven without route movement, contract change, or orchestration-risk expansion.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### CL-2026-06-25-API-GROUPING-FACADE-SEARCH-VERTICAL-SLICE: active public search results grouping and service adoption
|
||||||
|
|
||||||
date: 2026-06-25
|
date: 2026-06-25
|
||||||
author: Cline
|
author: Cline
|
||||||
scope: `pages/api/search/basic-paged.js`, `pages/api/search/advanced.js`, `pages/api/search/advanced-paged.js`, `pages/api/search/basic-details-paged.js`, `actions/services/searchDirectService.js`, `tests/phase22/api-grouping-facade-search.test.cjs`, `context/api-grouping-adoption-roadmap.md`
|
scope: `pages/api/search/basic-paged.js`, `pages/api/search/advanced.js`, `pages/api/search/advanced-paged.js`, `pages/api/search/basic-details-paged.js`, `actions/services/searchDirectService.js`, `tests/phase22/api-grouping-facade-search.test.cjs`, `context/api-grouping-adoption-roadmap.md`
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
const assert = require("assert");
|
||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
|
||||||
|
const rootDir = path.resolve(__dirname, "..", "..");
|
||||||
|
|
||||||
|
const tests = [];
|
||||||
|
const test = (name, fn) => tests.push({ name, fn });
|
||||||
|
|
||||||
|
const exists = (relativePath) =>
|
||||||
|
fs.existsSync(path.join(rootDir, relativePath));
|
||||||
|
|
||||||
|
const read = (relativePath) =>
|
||||||
|
fs.readFileSync(path.join(rootDir, relativePath), "utf8");
|
||||||
|
|
||||||
|
test("known grouped facade folders exist for proven slices", async () => {
|
||||||
|
[
|
||||||
|
"pages/api/subscriptions",
|
||||||
|
"pages/api/documents",
|
||||||
|
"pages/api/search"
|
||||||
|
].forEach((folderPath) => {
|
||||||
|
assert.strictEqual(exists(folderPath), true, folderPath);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("known façade characterization test files exist", async () => {
|
||||||
|
[
|
||||||
|
"tests/phase22/api-grouping-facade-subscriptions.test.cjs",
|
||||||
|
"tests/phase22/api-grouping-facade-documents.test.cjs",
|
||||||
|
"tests/phase22/api-grouping-facade-search.test.cjs"
|
||||||
|
].forEach((filePath) => {
|
||||||
|
assert.strictEqual(exists(filePath), true, filePath);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("existing slice tests cover service adoption or retained scope checks", async () => {
|
||||||
|
const subscriptionsSource = read(
|
||||||
|
"tests/phase22/api-grouping-facade-subscriptions.test.cjs"
|
||||||
|
);
|
||||||
|
const documentsSource = read(
|
||||||
|
"tests/phase22/api-grouping-facade-documents.test.cjs"
|
||||||
|
);
|
||||||
|
const searchSource = read(
|
||||||
|
"tests/phase22/api-grouping-facade-search.test.cjs"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.match(
|
||||||
|
subscriptionsSource,
|
||||||
|
/portalDirectService watched-case methods now target subscriptions facade routes/
|
||||||
|
);
|
||||||
|
assert.match(
|
||||||
|
documentsSource,
|
||||||
|
/searchDirectService document journey methods now target grouped documents facade routes/
|
||||||
|
);
|
||||||
|
assert.match(
|
||||||
|
searchSource,
|
||||||
|
/searchDirectService active public search methods now target grouped search routes/
|
||||||
|
);
|
||||||
|
assert.match(
|
||||||
|
searchSource,
|
||||||
|
/excluded routes remain outside grouped public search adoption scope/
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("checkpoint tests do not imply legacy handlers can be removed", async () => {
|
||||||
|
const sliceSources = [
|
||||||
|
read("tests/phase22/api-grouping-facade-subscriptions.test.cjs"),
|
||||||
|
read("tests/phase22/api-grouping-facade-documents.test.cjs"),
|
||||||
|
read("tests/phase22/api-grouping-facade-search.test.cjs")
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
|
assert.doesNotMatch(sliceSources, /legacy handlers can be removed/i);
|
||||||
|
assert.doesNotMatch(sliceSources, /delete legacy/i);
|
||||||
|
assert.match(sliceSources, /legacy .* remain/i);
|
||||||
|
|
||||||
|
const roadmapSource = read("context/api-grouping-adoption-roadmap.md");
|
||||||
|
assert.match(roadmapSource, /does not approve route removal/i);
|
||||||
|
assert.match(
|
||||||
|
roadmapSource,
|
||||||
|
/legacy handlers remain present and canonical/i
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
async function run() {
|
||||||
|
let failures = 0;
|
||||||
|
|
||||||
|
for (const { name, fn } of tests) {
|
||||||
|
try {
|
||||||
|
await fn();
|
||||||
|
console.log(`✓ ${name}`);
|
||||||
|
} catch (error) {
|
||||||
|
failures += 1;
|
||||||
|
console.error(`✗ ${name}`);
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (failures > 0) {
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
run();
|
||||||
Reference in New Issue
Block a user