Zenwealth

ZenWealth is a personal finance app built with ASP.NET Core and React. It connects to banks via Plaid to track expenses, manage budgets and analyze spending.

TypescriptReactC#Asp.Net CoreSQL ServerTailwind CSSShadcn
Zenwealth

The Problem

Many people struggle to understand where their money goes each month. The standard way a lot of people track their expenses is through an Excel sheet where they manually export the transactions from their bank then import it into the Excel sheet. This can be time-consuming and error-prone. Due to this, a lot of people don't regularly review their financial health.

I wanted to create a solution that automatically aggregates financial data, and all it requires the user to do is to connect the bank account that they want to track. I also wanted the interface to be intuitive and visually appealing, making it easy for users to understand their financial health at a glance.

A Standard Excel Budget Template
A Standard Excel Budget Template

My Solution

ZenWealth is a personal finance application that connects to users' bank accounts via Plaid, allowing them to track expenses, manage budgets and analyze spending patterns. The app provides a clean, user-friendly interface for monitoring financial health without the hassle of manual data entry.

Plaid

To securely connect to users' bank accounts, I integrated Plaid, a solution that simplifies access to banking data. With Plaid, developers can retrieve information like transaction history and account balances without ever handling or storing sensitive credentials.

Plaid uses OAuth, an industry-standard protocol that ensures users stay in control of their data. As a developer, I can request only the specific permissions needed — for example, access to transaction history, account balances or basic account details. This means users can confidently link their bank accounts to ZenWealth without worrying about exposing their login information.

Because authentication happens directly through the user's bank, credentials remain private. ZenWealth never sees or stores them. Instead, Plaid securely provides access tokens that let the app do its job while keeping user data safe and protected.

Plaid Link InterfacePlaid Link InterfacePlaid Link Interface
Plaid Link Interface for connecting bank accounts

Backend Architecture

I built the backend of this website using ASP.NET Core, drawing on my previous experience with the framework. At first, I followed a conventional layered architecture by dividing the project into Presentation, Service and Data layers.

Upon exploring software design patterns, I came across the principles of Hexagonal Architecture, also known as Ports and Adapters. The benefits such as greater testability and improved scalability were compelling.

I decided to refactor the backend accordingly to align more with hexagonal principles. This also allowed me to create basic unit and integration tests for the application.

Hexagonal Architecture
Hexagonal Architecture

To optimize performance and reduce reliance on external APIs, transaction data from the Plaid API is stored in a database. This data is efficiently updated via Plaid webhooks whenever new information becomes available, preventing the overhead of redundant API requests.

Database Design

I started with SQL Server for my database, but the need for more convenient hosting led me to Postgresql.

My initial hosting attempt with Azure's free tier proved problematic. Database queries frequently timed out because the free tier automatically powered down the server during periods of inactivity, which was a big issue for end users.

To resolve this, I switched to a Postgresql database hosted on my Hetzner VPS. This allowed me to maintain a continuously running database server without constant idling.

For the Database Schema I used five different tables:

  • Users - Stores user information and authentication details.
  • Accounts - Contains information about the bank accounts linked to the user.
  • Transactions - Records individual transactions associated with each account.
  • Budgets - Manages user-defined budgets for different categories.
  • Items - Represents the institutions (banks) linked to the user's accounts.
Database Schema
Database Schema

To support scalability, I implemented keyset pagination when querying transactions. Unlike traditional offset-based pagination, which becomes increasingly inefficient as the dataset grows, keyset pagination enables efficient retrieval of large amounts of data without performance degradation. This approach is especially valuable when working with long and ever-growing transaction histories.

For data access, I used Entity Framework Core to write and manage SQL queries. One of the key advantages of EF Core is its provider-agnostic design, which allows the underlying database to be swapped out without rewriting query logic.

This flexibility aligns well with hexagonal architecture principles, making it easier to introduce a new adapter for a different database provider if needed, without affecting the core business logic or other parts of the application.

Email Notifications

To keep users informed about their financial activities, I implemented an email notification system using Azure. This system sends notifications and verification emails to users, enhancing the overall user experience and security of the application.

Frontend Architecture

The frontend of ZenWealth is built using React, TypeScript and Tailwind CSS. I chose to use Tailwind CSS as it allows for rapid development of responsive and visually appealing user interfaces. Shadcn UI components were used as they are easily customizable and are already built with Tailwind CSS, allowing me to focus more on the functionality rather than the styling.

For the charts I used Recharts which is a composable charting library built on React components. It provides a simple and flexible way to create charts and graphs, making it easy to visualize financial data.

Shadcn Components
Shadcn Components

The frontend communicates with the backend via a JSON API, as Plaid advises using a separate backend and frontend so that the backend can securely handle certain actions.

A good example of this is the Link flow that authorizes the user's bank account. The process is as follows:

  • A Link token is retrieved from the backend and supplied to the Plaid Link component in the frontend.
  • Once the user completes their bank authorization, they are supplied with a public token which needs to be returned to the backend and expires after a short period of time.
  • Once the backend has the public token, it's swapped for an access token which expires after a longer period of time and is used to make requests to the users' bank.

Dashboard

The dashboard page is the first thing that the user sees when they log in, so I decided to make it give the user an overview of their financial health. It shows the user's various monthly metrics at a glance like the balance, expenditure, savings and income in the first section.

ZenWealth Dashboard

In the following section, the user can see their recent transactions, and they get the option of viewing both income and expenses together or separately. They also get a line graph of how their income compares to their expenses over the past year.

Finally, it displays a bunch of other cards that display things like the budget status, liabilities and top expense categories.

Accounts

The accounts page is all about the connected bank accounts, so I made sure to highlight the user's current Net worth as the first thing they see when on the page. Afterward, they can view two pie charts which contain their assets and liabilities.

ZenWealth Accounts

Throughout the page they get different ways to view the accounts either through an accordion or a bar chart. Clicking on the pie chart sector or the bar chart section scrolls and highlights the relevant account in the accordion.

Transactions

Arguably the part I spent the most time on, the transactions page is where users can view all their transactions in a table. They can filter the transactions by date, account and category. They can also search for specific transactions by name and sort by either the date or amount.

ZenWealth Transactions

The UI of the table was created using Tanstack table, which makes it easy to create a responsive and interactive table. It also allows for features like sorting, filtering, pagination and column visibility.

Budgets

On the budgets page, users can create and manage their budgets. They can set a budget for a specific category and track their progress towards that budget. The page also shows the user's total budget and how much they have spent so far.

ZenWealth Budgets

I also give the user the option to set a different start date for their budget so that they can align it with their pay cycle. This is especially useful for users who get paid bi-weekly or on a different day than the end/beginning of the month. They can also change the view to a table view which shows the data in a different format.

Analytics

The first chart provides a detailed breakdown of the user's income and expenses over the past year. It displays the total income, total expenses, and net income (income minus expenses) for each month.

ZenWealth Analytics

To enhance interactivity, users can hover over any month to reveal a category-level breakdown of their income and expenses for that period. This gives users a clear and intuitive view of how their finances have evolved month by month.

The second chart is a rose chart that highlights the top six expense categories for the selected time period. It offers a clear visual representation of the user's spending patterns, making it easy to identify which categories take up the largest portions of their budget.

ZenWealth Analytics

Each segment displays both the percentage and the total amount spent in that category, giving users an at-a-glance understanding of where their money is going.

Security & Privacy

For authentication and authorization in the application, I integrated ASP.NET Core Identity. I opted for cookie-based authentication over JWTs, as I found it more straightforward to implement and better suited to my understanding at the time.

ZenWealth Account Settings

To ensure user privacy, all sensitive data queries are scoped to the currently authenticated user, effectively preventing any possibility of accessing another user's financial information.

To enhance account security, I added multifactor authentication (MFA). This helps mitigate the risk of unauthorized access due to compromised passwords—especially those exposed in data breaches.

ZenWealth Security Settings

Finally, the application gives users full control over their data. At any time, they can delete their account or remove a connected bank account, along with all related transactions.

Lessons learnt while building the project

  • Authentication & Authorization with ASP.NET Core Identity - Gained hands-on experience integrating ASP.NET Core Identity into a web application. Explored both cookie-based and JWT-based authentication methods, understanding their differences and use cases.

  • Advanced Table Components with TanStack Table in React - Built dynamic, reusable table components using the TanStack Table API. Implemented features such as sorting, filtering, column visibility toggles and pagination to improve data interaction in the UI.

  • Database Indexing and Query Optimization - Learned about clustered vs. non-clustered indexes, and the difference between index seeks and index scans. Applied this knowledge to optimize SQL queries for performance.

  • Software Architecture Patterns - Studied and compared patterns like Clean Architecture, Hexagonal Architecture and N-Tier Architecture, evaluating their benefits, trade-offs and suitability for different types of applications.

  • Working with Third-Party APIs - Practiced consuming external APIs and transforming the returned data into frontend-friendly formats, ensuring smooth integration between backend services and the React UI.