Fem personer, fortfarande i alpha. Backend och drift gör jag på fritiden.
vad
Compys hjälper startups att hitta juniora utvecklare, praktikanter och första-anställningar genom personlig matchning i stället för ansökningsflod. Ett fåtal relevanta kandidater per roll — inte hundratals CV:n att sålla i.
Mitt jobb är allt som händer bakom knapparna: API:et som driver matchningen, databasen där profiler och roller lever, och infrastrukturen som håller det igång på Azure.
rollen
Som backend lead sätter jag den tekniska riktningen: arkitektur, kodstandard och de riktlinjer teamet bygger efter. Som delägare bär jag också ansvaret för att det vi levererar faktiskt håller — skalbart, säkert och möjligt att förvalta.
ansvar.md
stacken
Ett litet team har inte råd med teknik som bara en person förstår, så kärnan är medvetet konventionell: .NET 8 och C#. Det intressanta ligger i valen ovanpå — rätt databas per domän, riktig molnplattform och en arkitektur som går att bygga vidare på utan att riva grunden.
Datan är polyglot: MongoDB där det är dokumenttungt och föränderligt, PostgreSQL via EF Core där relationer och transaktioner räknas. Auth via Firebase och JWT, realtidsnotiser via SignalR, betalningar via Stripe — allt hostat på Azure.
# .NET 8 web api — vertikala slices, en mapp per domän (~27 st) Features/<Domain>/ Application/ CQRS — Commands + Queries + handlers (MediatR) Domain/ entiteter · value objects · domänhändelser Infrastructure/ repositories · workers · mappning Controllers/ tunna endpoints Common/Behaviors/ pipeline: validering + caching, en gång för alla data MongoDB + PostgreSQL (EF Core) # rätt verktyg per domän auth Firebase + JWT realtime SignalR host Azure iac Terraform
arkitektur
Backenden är ett trettiotal vertikala slices — en mapp per domän som äger hela vägen från request till svar, i stället för lager som skär tvärs igenom allt. Läsning och skrivning är åtskilda med CQRS via MediatR: queries hämtar, commands ändrar.
Det som annars upprepas i varje endpoint — validering, caching, loggning — ligger som pipeline-behaviors runt varje anrop. En query behöver bara säga att den får cachas; behaviorn sköter resten, en gång för alla.
CachingBehavior.cs
// En query är bara data — den vet inget om cache, validering eller loggning. public record SearchProfilesQuery(string? SearchQuery, int PageSize /* … */) : ICacheableQuery<SearchProfilesResponse> { public string CacheKey => $"profiles:{SearchQuery}:{PageSize}"; public TimeSpan? Expiration => TimeSpan.FromMinutes(5); } // En pipeline-behavior lägger cachen runt varje sådan query — en gång, för alla. public class CachingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : ICacheableQuery<TResponse> { public Task<TResponse> Handle(TRequest req, RequestHandlerDelegate<TResponse> next, CancellationToken ct) => _cache.TryGetValue(req.CacheKey, out TResponse? hit) && hit is not null ? Task.FromResult(hit) : CacheAndReturn(req, next); }
bakgrund
Poäng, topplistor och aviseringar ska inte räknas medan användaren väntar på svar. De läggs på en kö och plockas av en BackgroundService-worker som kör själva jobbet som ett eget MediatR-command — samma mönster som allt annat, fast utanför request-tråden.
event någon röstar på ett inlägg └─ queue PostId läggs på en in-process-kö └─ worker BackgroundService plockar av kön └─ mediator.Send(UpdatePostScoreCommand) # utanför request-tråden └─ ny score sparas · SignalR pushar ut till klienten
Vår marketing-person Mio ville kunna skicka snygga, stylade mejl till partners och intressenter utan att gå via mig varje gång. Så jag byggde EmailDesigner: ett litet fristående verktyg där hon loggar in (Firebase), bygger mejlet visuellt och skickar — rakt genom vår egen backend.
Inget externt mejl-SaaS. Backendens EmailDomain renderar mallar med Scriban, levererar via Azure Communication Services och loggar varje utskick med status. Samma command/handler-mönster som resten av API:et: ett SendCommunicationEmail-command in, ett spårbart EmailLog ut.
infra
Infrastrukturen är mitt bord, och den bor i Terraform — inte i portalens klickträd. Hela miljön går att läsa, granska och återskapa. Secrets ligger i Key Vault och aldrig i koden, filer i Blob Storage, loggarna i App Service.
Stripe-webhooks körs idempotent: samma event kan komma två gånger utan att något dubbelbokförs. Det som ska funka testas — både unit- och integrationstester. Samma princip som i homelabben: automatisera det tråkiga, dokumentera resten. Fortfarande en alpha byggd på fritiden, men byggd för att växa.
/* ett riktigt projekt, inte en demo — därför står det bara det jag kan visa publikt */