Building Portfolio Insights: Lessons from an Event‑Driven .NET Microservices Dashboard
Source: Dev.to
Portfolio Insights – Personal Finance Dashboard
Built with .NET 8, RabbitMQ, gRPC, Docker (micro‑services architecture)
Overview
Portfolio Insights is a demonstration of a .NET 8 micro‑services‑based personal‑finance and analytics platform. It builds on the architectural patterns explored in my earlier E‑Commerce Microservices project, applying them to a number‑driven, analytics‑focused domain.
The application lets users:
- Manage investment portfolios (assets & quantities)
- Retrieve simulated market prices
- Compute portfolio analytics asynchronously
- Receive notifications when analytics are refreshed
All services are containerized and orchestrated via Docker Compose, showcasing a realistic end‑to‑end distributed system.
What I Learned
- Event‑driven microservices
- gRPC and HTTP communication
- CQRS & layered architectures
- Message‑based analytics pipelines
- Vertical Slice & Clean Architectures
- Container orchestration with Docker
- Cross‑cutting concerns (validation, logging, health checks, custom exception handling)
Solution Architecture
The solution consists of eight projects, all at the same directory level:
| Service | Responsibility | Data Store |
|---|---|---|
| Market Data Service | Simulates & publishes market prices | SQLite / Redis / In‑memory |
| Portfolio Service | Owns user portfolios & assets | PostgreSQL (Marten) |
| Analytics Service | Computes metrics from portfolio & market events | SQL Server (layered architecture) |
| Notification Service | Reacts to analytics results & stores notifications | SQLite |
| YARP API Gateway | Single entry point for all client traffic | – |
| Razor Pages Web Client | UI that consumes the system via the gateway | – |
| (Other supporting projects) | Infrastructure, contracts, etc. | – |
High‑Level Flow
- User adds/updates assets in their portfolio.
- Portfolio Service publishes a
PortfolioUpdatedEvent. - Analytics Service consumes the event, joins it with the latest market prices, and computes metrics.
- Analytics Service publishes an
AnalyticsComputedEvent. - Notification Service reacts and stores a user notification.
- The client queries the Analytics or Notification endpoints when the data is ready.
Communication Styles
- gRPC – Low‑latency, request/response interactions (e.g., Portfolio Service → Market Data Service for current prices).
- RabbitMQ + MassTransit – Event‑driven workflows (portfolio updates, market price changes, analytics computations).
Design Insights
Diagram First
Before writing code, I mapped out:
- Data ownership per service
- Synchronous vs. asynchronous interactions
- State‑change propagation across the system
This upfront work prevented costly redesigns and clarified endpoint contracts.
Asynchronous UI Considerations
Because analytics are computed in the background, the Razor Pages client must:
- Trigger commands that start background workflows
- Display previously computed data while waiting
- Refresh views once downstream services have processed events
Designing the UI forced me to think carefully about when to request data and which service should provide it.
Service‑Specific Storage
| Service | Storage Choice | Rationale |
|---|---|---|
| Portfolio | PostgreSQL (Marten) | Document‑store semantics for flexible portfolio shapes |
| Analytics | SQL Server | Relational queries for complex metric calculations |
| Market Data | SQLite / Redis / In‑memory | Light‑weight persistence for simulated price feeds |
| Notification | SQLite | Simple, durable storage for user alerts |
The key principle: clarity of ownership outweighs strict consistency across services.
Running the Project
# Clone the repository
git clone https://github.com/yourusername/Portfolio-Insights.git
cd Portfolio-Insights
# Start all services with Docker Compose
docker compose up --build
The gateway will be reachable at http://localhost:5000, and the Razor Pages UI at http://localhost:5000/ui.
Final Thoughts
Portfolio Insights is a learning and portfolio project, but it represents a meaningful step forward in my understanding of modern .NET micro‑services:
- Clear boundaries and explicit contracts are essential in event‑driven systems.
- Early workflow diagramming saves time and reduces coupling.
- Front‑end design must reflect the realities of asynchronous processing.
- Docker Compose provides an excellent sandbox for reasoning about distributed systems as a whole.
Feel free to explore the code, spin up the containers, and experiment with the architecture!
Microservices, Asynchronous Communication, and System‑Level Design
If you’re exploring similar architectures, my biggest recommendation is simple: design the flows before the code. In event‑driven systems, that clarity makes all the difference.
What’s Next?
If this article gave you a clearer picture of how an event‑driven microservices architecture comes together in a real‑world .NET application, here are a few ways to continue exploring:
- 📁 Explore the GitHub Repository – Review the full source code to see how the services, message flows, API Gateway, and Razor Pages client fit together. Following the code alongside the architecture diagrams is the best way to reinforce how synchronous and asynchronous communication work in practice.
- 💡 Share the Architecture – If you know someone learning microservices, messaging, or distributed systems in .NET, share this project with them. It’s often easier to understand these concepts through a concrete, end‑to‑end example.
- 🖥 Experiment and Extend – Clone the project and try evolving it. Add new events, introduce additional analytics, or adjust how the client reacts to asynchronous workflows. Small changes are a great way to deepen your understanding of event‑driven design.
- 👍 Star the Repository – If you find the project useful, consider starring the repository. It helps others discover the project and supports continued sharing of practical architecture examples.
Working through projects like this builds intuition that goes beyond individual frameworks—helping you design systems that scale in complexity while remaining understandable.