Layered Serverless Architecture
Migrating from a monolith to a clean MVC-like pattern (Routes → Controllers → Services → Repositories).
FinTech / Selected work
Backend development for a platform connecting founders and investors.
All workMicroFundry brings together campaigns, investor activity, founder uploads, and administrative workflows. Our contribution focused on the backend that connects these parts.
The work separated routes, controllers, services, and repositories so changes to one area could be understood and tested more clearly.
Pitch decks and campaign media need a different path from ordinary API requests. Direct uploads to S3 use presigned URLs, allowing files to reach storage without passing through the application's request body.
Investment and withdrawal workflows use database transactions to group related changes. Administrative actions are recorded alongside the workflow, giving the team a history to refer to.
Scheduled operations handle recurring work such as checking campaign dates. The resulting structure gives the team a clearer place to add and maintain product features.
A closer look
Engineering decisions
Migrating from a monolith to a clean MVC-like pattern (Routes → Controllers → Services → Repositories).
Building atomic, transactional logic for investments, withdrawals, and referral payouts to ensure data integrity.
Implementing S3 presigned URLs to bypass serverless limitations and enable large, secure media uploads.
public async processWithdrawal(adminId: string, withdrawalId: number, status: "approved" | "rejected", notes: string) {
return db.transaction(async (tx) => {
// 1. Verify the request is valid and pending
const withdrawal = await this.userRepo.findWithdrawalById(withdrawalId, tx);
if (!withdrawal || withdrawal.status !== "pending") {
throw new AppError("Request not found or already processed.", 404);
}
// 2. Update status and log the admin's action for audit trail
const updated = await this.userRepo.updateWithdrawalRequest(withdrawalId, { status, adminNotes: notes }, tx);
await this.adminRepo.createAdminLog(adminId, `withdrawal_${status}`, { notes }, tx);
// 3. Notify the founder
await this.notificationService.createInAppNotification({ /* ... */ }, tx);
return updated;
});
}
A good place to begin
A new idea, an existing product,
or a problem worth solving.