Based on the requirements and prompt I felt that I had a good understanding of what I wanted the API to look like and how the service should be structured.
I also had a decent idea of how I wanted the UI to look and behave.
For each of these components I had strong opinions about what I thought should be the minimum viable cases to cover.
I also had strong opinions on how the API service should be written, but less so about the UI.
While I have decent familiarity with React/Vue code architecture I don't have enough experience to form as strong opinions as I have on the backend.
- .NET 8
- npm
- Start the API
dotnet run --project TodoApi
- Install and start the UI
npm i --prefix client npm run dev --prefix client
- Visit the app at http://localhost:5173
- Check out the Swagger doc at http://localhost:5189/swagger
- Vue 3
- .NET 8
- EFCore
- SQLite
With all that being said, I wanted to spend the majority of my time
- focusing on the backend to make sure it is exactly how I would want it structured
- focusing on the UX of the frontend to test out behavioral edge cases
For the backend, I started off using AI to scaffold the skeleton but found that it was too far away from what I wanted so I wrote most of it by hand.
Pretty much the only things on the backend that are AI-generated are the filters, middleware, validation, and startup bootstrapping. I've used all of those implementations pretty extensively in the past but it's been a while since I've written C# so figured it would be faster to have AI write that boilerplate for me.
I also used AI to generate the TodoApi.http file.
I heavily used AI to write all of the frontend code. I reviewed all of the code it generated, but was more declarative in how I prompted it and evaluated the results.
I gave it what I wanted the behavior and look to be, how I wanted the composables and components to be organized, and to use an openapi spec to codegen the API client.
I want to point out that I probably could not produce the frontend code as it exists right now by myself in a reasonable amount of time without a lot of searching for answers on syntax, lifecycle hooks, and property propagation.
Listed are the assumptions and requirements on which I based this project.
- Contract-first design (I started everything off with the API contract and built from there)
- Resource-oriented API design
- Vertical slice architecture (I largely prefer this over the typical MVC pattern)
- No repo/DAL layer since the ORM can be considered as such
- Paged responses to avoid large db queries and ORM overhead
- Integer IDs instead of UUIDs to simplify paging at the cost of an enumeration vulnerability
- Error handling middleware to avoid leaking internal error stack details
- DTOs and layer separation to maintain domain boundaries
- Minimal look and feel
- Light usage of coloring, spacing, and lines to impart enough distinciton between components and actions, but not overly distract from the focus areas for the user
- "Create" fields at the top so the user doesn't have to scroll down a long list to add a new item
- Autosave and drag-and-drop item reordering for predictable behavior
- "Disabled" look on existing items to indicate they are safe to click and interact with without accidentally updating them
- Wrapped text and dynamic height resizing to avoid truncated or horizontally scrolling text fields
- Separation of components and composables to split up view and logic responsiblity
- Codegen API client to keep the API service the source of truth
- Debounced API calls to avoid DOSing the service
I decided to ignore these because they either added too much infrastructural busywork or added more complexity than I thought was necessary to convey my general approach to building things.
- Concurrency control
- Caching
- List sharing
- Versioning
- SDK DX
- Mobile UX
- Deployment
- Networking
- Security (AuthN, AuthZ, CORS, etc)
- Observability and monitoring