Series: The Budgeting React App

TL;DR

I have turned my brother’s bank transaction script into a React/Python web app to learn advanced industry tech. To challenge myself, I intentionally over-engineered the backend using hexagonal architecture, Domain-Driven Design, and Python protocols, but am now refactoring out a problematic singleton pattern to resolve circular import issues.

Background

Initially my brother developed a chat-gpt-special python script to pull bank transactions from his bank using plaid. The script took these transactions and translated them into a beautiful Excel spreadsheet, and even supported automatic categorization.

Being the nerdy engineer (yes I know that is redundant) that I am, I decided to turn his script into a web app, because why not. I decided I wanted a React frontend and a Python backend since this is common in the industry. While a working product would be a huge win for my brother, I’m excited to learn in-depth knowledge about the related technology.

Design Patterns

Looking at the backend, one might notice: “this code base is overly complicated.” I am aware; that is intentional.

I chose to experiment with implementing a hexagonal architecture for the backend. Because this is not a native python concept, I am forced to write a lot of the implementation myself. There are no libraries or frameworks that do this automatically, for example like Java. You will see artifacts of the Adapter Pattern and DDD (Domain-Driven Design), notice the “BudgetLimitAggregate” class in “base_models.py” for example.

The “ports” module is a primary feature of hexagonal architecture. The classes therein are simply python protocols, which are used for their intended purpose: plug-and-play, duck typing. Protocols were used to allow for flexibility, as is the way of Python. I wanted to specify a contract that could be used throughout the application for adapters without overcomplicating things with hierarchical boilerplate code.

Another concept I played with is the singleton pattern. There are more Pythonic ways to accomplish my needs (e.g. the adapter instantiation need). However, I through that in there for fun. There is functional resistance to the singleton pattern in Python, because it doesn’t have a global context like many purely object oriented languages. If you need a global resource you must implement it in a top-level module and import that module where you need it.
I am considering refactoring this out because it is causing too many circular import issues. I think it is a wise technical decision to lean more pythonically and instantiate a new adapter on a per-usage basis.

Leave a Reply