Aplyra
A TypeScript monorepo built as an architecture exercise: strict hexagonal design, interchangeable AI providers, similarity-based caching, and 243 tests against real services.
Why it exists
AI résumé generators usually have two problems: (1) they hallucinate — adding experiences, certifications or technologies that don’t exist — and (2) they re-call the model every time you ask for a small change, which costs money and time.
Aplyra is my attempt to fix both. I also built it to push my own idea of how disciplined a hexagonal architecture in TypeScript can be when no one is pressuring you to take shortcuts.
The decisions that matter
Strict hexagonal, enforced by ESLint. Four ports define the only ways the domain talks to the outside world (AI, persistence, rendering, search). Custom ESLint rules block adapter imports from the domain — the commit doesn’t land if you break the architecture.
Two interchangeable AI providers. OpenAI and Gemini, behind the same port interface. Switching provider is switching an env var; the domain never finds out.
Anti-hallucination guard. After generation, I compare the output résumé against the input facts. If jobs, dates or technologies appear that weren’t in the source, the response is rejected with HTTP 422 before it ships.
Jaccard similarity + SHA-256 cache. Exact hash first (returns in milliseconds); if no match, Jaccard similarity over the input token set. Very similar inputs reuse the last model response. LLM spend drops sharply under real usage.
Serious testing
243 tests across three levels: unit over the pure domain, integration against real MongoDB (no mocks) and real Puppeteer for PDFs, and e2e across the whole chain. No mocks of external providers that lie — if Mongo goes down in CI, the test goes down, and that’s information.
This is deliberate. Mocking infrastructure gives you green tests and broken production; testing against real services gives you honest feedback and a slightly slower CI that’s worth every second.
What I learned
That architectural discipline isn’t optional when you work alone. Without code review, the only thing protecting you from shortcuts is making shortcuts technically impossible — via linters, types, and explicit barriers.
Also that smart caches are a very cheap form of building product. Every LLM call you avoid is money, latency and carbon footprint. A 200-line Jaccard cache is worth more than almost any prompt optimization.