Simple Leasing, Part 1: The Project
The last few posts have been theoretical; this one isn’t. Let’s start a project from scratch, building on the principles of the COILS architecture I proposed in the last post. (If you haven’t read “Building Bridges” and “The Missing Middle” yet, go do that, then we can get started.)
Business Requirements
The first task for any project is eliciting business requirements from stakeholders. COILS isn’t intended to prescribe a methodology for that part of a project, but it does suggest that you should end up with domain stories that function as sentences written in the language of the system that you want to write.
Sticking with the apartment leasing system, that might look like this:
- An administrator configures the system with the leasable assets.
- An administrator configures users for managers.
- A prospective tenant searches for available apartments.
- A manager records a signed lease.
- A tenant pays his rent.
- A manager generates notices for unpaid rent.
- A manager generates occupancy reports.
From this set of sentences, nouns and verbs start to emerge:
- Administrators are concerned with making sure that the system is functional for the day-to-day users.
- Managers are responsible for helping prospective tenants sign a lease and making sure they pay rent.
- Tenants must pay their rent.
Notice, too, that technical details are also suggested by the different responsibilities:
- Administrative tasks will need their own interface (or “execution context”).
- Managers will need to be authenticated1 and authorized2 for certain tasks.
- Similarly, tenants will need to be authenticated and authorized…
- …but not prospective tenants.
We don’t necessarily need to implement the most technically complex parts of the system at first, so we can pick and choose what we want to focus on. I usually like to focus on administrative tasks first, so that we can slowly build a working system from the ground up. It’s hard to do that if you haven’t built a way to interact with the data at the ground level.
The Project
This example is going to use TypeScript, pnpm
for package management, and jest
for unit tests. Optionally, you can also use prettier
for formatting. Describing how to set up projects in this environment is beyond the scope of this article, and can be a moving target in the Node ecosystem, but suffice to say that after the initial project setup you’ll end up with a directory structure something like this:
simple-leasing/
package.json
pnpm-lock.yaml
tsconfig.json
The structure of the source tree is entirely up to us, and this is where many beginners start to falter. “Middle-of-the-river” methodologies often prescribe pattern names, but without explicitly illustrating how this maps to the practical reality of the source tree. That’s because it’s a matter of taste and preference, but they never quite come out and say that! For myself, I like to keep the top of my source hierarchy relatively clean and use nested “modules” for different concerns. As a bonus, this makes it easier to split a project into different source artifacts if that becomes desirable. If you’re using a Unix-based system, you can run mkdir -p src/{common,core,state,web,cli}
and end up with my preferred structure:
src/
cli/
common/
core/
state/
web/
Some of these might not be obvious:
cli
will contain a command line interface (CLI) for the system. This will likely be the only place where administrative tasks are available, in this example. Authentication and authorization are implied if you have access to an execution environment where a CLI can impact the system! The CLI will be an implementation of the OI part of COILS.common
is for utility types and associated functions. This is where cross-cutting concerns like error handling and logging can be placed. In practice, almost all of the system — including some parts of the purecore
— will need access to these types.core
is where pure business logic and its coordinator(s) (the C and L in COILS) will reside. You can think of coordinators as the public interface to this module.state
will contain multiple impure interfaces to the outside world, and represents the S in COILS. Think database connections, file handling, and external integrations.3web
is the primary interface for tenants and property managers. This is another implementation of the OI part of COILS.
What’s Next?
Now that we have a basic project setup, the next article will cover how to begin fleshing out this skeletal architecture. We’ll begin with the core business logic, writing significant parts of the C and L (and likely touching on state interfaces). Until then, I encourage you to set up this structure and think about what pure functions we might want to implement the domain stories listed above. Try to get comfortable with the ecosystem, too, if you’re not already. It’s hard to follow along if you’re struggling with your tools!
-
To authenticate a user means to make sure that they are who they say they are, often using username and password. ↩︎
-
To authorize a user means to make sure that they have access to perform a particular action or to see certain data. ↩︎
-
This example will hand-wave integrations, but good examples for a leasing system include credit screening and criminal background checks for the conversion of a prospective tenant into a signed tenant. ↩︎