BuildCv.Domain
The entities, the weights and the bands. No outward dependencies: this is where the definition of a match lives, and it can be tested without starting anything.
BuildCv scores a résumé against one specific job posting and explains where every point came from. No language model anywhere in the calculation: the weights, the thresholds and the rules are published, and the same CV and posting return the same number today and next month.
Job seekers send dozens of applications and hear nothing back. The tools promising to 'optimise your CV for the ATS' return a number with no explanation, and when there is an LLM behind it the same CV can score 68 today and 74 tomorrow with nothing changed. A score you cannot reproduce is not a diagnosis — it is an opinion formatted as data.
Real hexagonal architecture, not folders named after layers. Dependencies point inward: the domain knows nothing about the database or about HTTP. The number beside each layer is its test-method count.
The entities, the weights and the bands. No outward dependencies: this is where the definition of a match lives, and it can be tested without starting anything.
The scoring engine and the recommendation builder. Orchestrates the domain and declares the ports that infrastructure implements.
Implements the ports: repositories, migrations, encryption. Its tests run against a real SQL Server started by Testcontainers, not against a double.
The HTTP surface. Secure by default: the global authorization policy requires a session and opting out is explicit with AllowAnonymous.
The browser never talks to the API. Tokens live in httpOnly cookies on this origin and every call goes through a route handler acting as a backend-for-frontend.
Six sections, each carrying a fixed share of the total. The bar is that share: Skills fills 45% of it because it weighs 0.45 of the score. Anyone can take these numbers and check their own result.
| Skills | 0.45 | |
|---|---|---|
| Experience | 0.20 | |
| Education | 0.10 | |
| Certifications | 0.10 | |
| Languages | 0.10 | |
| Projects | 0.05 |
A section the posting does not ask about consumes no weight: its share is redistributed proportionally across the sections it does ask about, so the ceiling is 100 for every posting. The previous version handed an unasked section a neutral 0.5, which made half its weight unreachable — a flawless CV scored 95 and the candidate had no way to find out why. In a product whose whole purpose is explaining the score, that was the worst possible bug.
When BuildCv suggests a change it does not estimate the benefit: it re-runs the whole formula with that single gap closed and reports the difference. A '+3.2 points' is a score that was calculated, not a forecast. If the engine cannot measure it, it does not claim it.
That the source compiles, that the image behaves, and that what is running is what you shipped are three different claims. All three are checked separately, and the third produced the most surprises.
Both repositories are public: the C# API and the Next.js client. Nothing claimed on this page requires you to take my word for it.
See all projects