Architecting the Digital Bookshelf
In this lab, you will take the Digital Bookshelf project from a plain-English business brief to a working, multi-model API. You will direct Claude in the AI as Architect role to design a data model, review that design as a Product Manager, implement the approved design yourself, and then use Claude Code to audit your implementation against the architecture record. This is the workflow professional teams increasingly use: the AI drafts, the human decides. Plan for approximately 2 hours of asynchronous work.
Scenario
A local library wants to modernize its book tracking system. They have hired you to build the backend for a new “Digital Bookshelf” application. When you arrive for the kickoff meeting, the library director hands you a written description of how the library operates: members, a catalog of titles, and a lending desk that has never lost a record in forty years. There is no field list, no schema, and no diagram. Turning that description into a correct data model, and then into a working API for managing the book inventory and recording loans, is your job.
Learning Objectives
By the end of this activity, you will have demonstrated your ability to:
- Direct an AI assistant in the Architect role to translate plain-English business requirements into an entity list, a relationship map, and draft Mongoose schemas.
- Evaluate an AI-generated data model against business requirements, write substantive change requests, and approve a revised design (the Product Manager role).
- Explain when related data should be embedded in a document and when it should be referenced with an
ObjectId. - Implement Mongoose schemas with appropriate data types, validation, and
ObjectIdreferences, and compile them into models. - Build a full CRUD (Create, Read, Update, Delete) API using Express with separate, modular routes, plus one endpoint that validates references between collections.
- Use Claude Code to audit an implementation against an approved architecture document.
Your Role and the AI’s Role
This lab uses the AI as Architect pattern. The division of labor is strict, and it is part of your grade:
- Claude (Architect): drafts the entity list, the relationship map, and the schema designs. It does not write Express routes, connection code, or any other application code.
- You (Product Manager, then Engineer): review the design, challenge it, request revisions, and give final approval. Then you write every line of implementation code yourself.
If Claude offers to write implementation code at any point, decline. The value of this exercise is learning to judge a design, not to accept one.
Part 1: Project Setup (10 minutes)
Set up the project with a modular structure that separates concerns: database connection logic in db/, Mongoose models in models/, and Express route definitions in routes/. Lab 1 fit in a single file; a three-model API does not.
-
Create and initialize the project, install dependencies, and create the directory structure:
mkdir digital-bookshelf-api cd digital-bookshelf-api npm init -y npm install express mongoose dotenv mkdir db mkdir models mkdir routes -
Create a
.envfile at the project root and add your MongoDB Atlas connection string (from the cluster you set up in Lab 1):MONGO_URI="your_connection_string_goes_here" -
Create a
.gitignorefile and addnode_modules/and.envto it:node_modules/ .env -
Create
db/connection.jswith your Mongoose connection logic:db/connection.jsconst mongoose = require('mongoose'); require('dotenv').config(); const uri = process.env.MONGO_URI; async function connectDB() { try { await mongoose.connect(uri); console.log('Successfully connected to MongoDB!'); } catch (error) { console.error('Connection error:', error); process.exit(1); } } module.exports = connectDB; -
Create a
server.jsskeleton. You will mount your routers here in Part 5:server.jsconst express = require('express'); const connectDB = require('./db/connection'); const app = express(); const PORT = 3000; app.use(express.json()); // Routers are mounted here in Part 5: // app.use('/api/books', bookRoutes); // app.use('/api/loans', loanRoutes); connectDB().then(() => { app.listen(PORT, () => { console.log(`Server running on port ${PORT}`); }); }); -
Create an empty file named
ARCHITECTURE.mdat the project root. This file is a graded deliverable; you will fill it in throughout the lab.
If you do not have access to a Pro-level Claude account through Per Scholas, you may complete this lab using the free tier of Claude or another AI assistant with equivalent capabilities. Free-tier usage limits may require you to complete the lab across more than one session. The architecture conversation in Part 3 is short enough that the free tier of claude.ai is sufficient for it.
Part 2: Requirements Intake (5 minutes)
Below is the business brief from the library director. Read it carefully. Notice that it contains no field names, no types, and no schema. Extracting those is the architecture work.
FROM: Dana Whitfield, Library Director
SUBJECT: What the new system needs to handle
Our members sign up in person. We take down their name, an email address,
and a phone number, and we note the day they joined. A member in good
standing can have up to five books checked out at any one time.
Our catalog is organized by title. For each title we keep the ISBN, the
author, the genres it belongs to, and how many copies we own. Copies of
the same title are interchangeable; we do not track individual barcodes.
Every checkout needs a record: who borrowed which title, the day it went
out, the day it is due, the day it actually came back, and any late fee
owed. Librarians also need to tell at a glance whether a loan is still
out, returned, or overdue.
One more thing: we never delete lending history. Some of our members have
been with us for decades, and we need to be able to pull up everything a
member has ever borrowed.Add the following section headings to ARCHITECTURE.md so the record has a consistent shape:
# Digital Bookshelf: Architecture Record
## Prompts Used
## Initial AI Design
## Product Manager Change Requests
## Approved Final DesignPart 3: AI Architect Session on claude.ai (25 minutes)
-
Sign in to claude.ai with your Per Scholas-provided Claude Pro account and start a new chat.
-
Paste the following role-enforcement prompt exactly as written, then paste the library director’s brief where indicated:
You are acting as my database architect for a MongoDB and Mongoose project. Your role is to design, not to build. You may produce: an entity list, a relationship map, and draft Mongoose schemas with validation rules. You may not write Express routes, controllers, database connection code, or any other application code, even if I ask for it. If I ask for implementation code, remind me that implementation is my responsibility in this exercise. For every relationship between entities, state whether the related data should be embedded or referenced, and justify the decision using the business requirements I provide. If any requirement is ambiguous, ask me clarifying questions before finalizing the design. Produce three deliverables, clearly labeled: 1. Entity list: every entity the system needs, with a one-sentence purpose for each. 2. Relationship map: each relationship between entities, the embed-or-reference decision, and the justification. 3. Draft Mongoose schemas: one per entity, with types, validation, defaults, and enums where appropriate. Here are the business requirements: [paste the library director's brief here] -
If Claude asks clarifying questions, answer them using only the information in the brief. If the brief does not answer a question, make a reasonable decision and note it; that is what a real Product Manager does.
-
When Claude delivers the three deliverables, copy your exact prompts into the Prompts Used section of
ARCHITECTURE.md, and copy Claude’s complete response into the Initial AI Design section. Do not edit or trim the response; the graded record must show the design as it was first delivered.
Do not skip ahead and implement this first draft. First drafts from an AI architect are usually plausible and frequently flawed in ways that only surface against the requirements. Finding those flaws is the next part of the lab.
Part 4: Product Manager Review (20 minutes)
Now change hats. As the Product Manager, you own the requirements, and you approve nothing that fails them. Audit the Initial AI Design against this checklist:
- Borrowing limit: Can the five-book limit be represented and enforced with this design? Where would that check happen?
- ISBN placement: Is ISBN uniqueness applied at the right level (once per title in the catalog, not on loans or members)?
- Loan status: Is the loan status restricted to a fixed set of values with an
enum(for example, checked out, returned, overdue), or is it free text a typo could corrupt? - Unbounded growth: Does any embedded array grow without bound? Remember the director: lending history is never deleted. Decades of loans embedded inside a member document is a design defect, because MongoDB documents have a size limit and unbounded arrays degrade performance. Loans should be referenced, not embedded.
- Validation completeness: Are
requiredfields,defaultvalues, and other validations complete? Is the due date required? Does the late fee default to zero? Is the join date set automatically?
Then complete the review:
- In the Product Manager Change Requests section of
ARCHITECTURE.md, write at least three substantive, numbered change requests. Each must state what to change and justify it against a specific line of the business brief. Substantive means the change affects correctness, data integrity, or scalability: changing an embed decision to a reference, adding a missing constraint, or fixing a wrong type all qualify; renaming a field does not. - Send your change requests back to Claude in the same chat and ask for a revised design.
- Review the revision. If it resolves every request, record it in the Approved Final Design section of
ARCHITECTURE.md. If it does not, push back again until it does. Only you can approve the design.
If Claude’s first draft happens to be strong, your change requests can tighten validation, add missing defaults, or challenge a justification you find weak. “The design was perfect” is not an acceptable review; every real design review produces findings.
Part 5: Implementation (40 minutes)
The design is approved. Now you are the engineer, and the AI writes none of this code.
A Short Primer: Referencing Documents
The lessons in this module covered schemas for a single collection. Your approved design connects three collections, so you need one new tool: the ObjectId reference. A field of type Schema.Types.ObjectId stores the _id of a document in another collection, and the ref option names the model it points to:
const mongoose = require('mongoose');
const { Schema } = mongoose;
const loanSchema = new Schema({
member: {
type: Schema.Types.ObjectId,
ref: 'Member',
required: true,
},
book: {
type: Schema.Types.ObjectId,
ref: 'Book',
required: true,
},
// ... the rest of your approved fields (dates, status, late fee) ...
});
module.exports = mongoose.model('Loan', loanSchema);When you query, the .populate() method replaces the stored ObjectId with the full referenced document:
// The stored member ObjectId is swapped for the full Member document.
const loan = await Loan.findById(someId)
.populate('member')
.populate('book');
console.log(loan.member.name); // the referenced member's data is availableThis is exactly why the unbounded-history requirement pushes loans into their own referenced collection: each loan is a small document pointing at its member and book, and history can grow for decades without bloating any single document.
Step 1: Models
- In
models/, createBook.js,Member.js, andLoan.js. - Implement each schema to match the Approved Final Design, including all types, validation rules, defaults, enums, and
ObjectIdreferences. Deviations will be flagged in the Part 6 audit and will cost points in grading. - Compile each schema into a model (
Book,Member,Loan) and export it.
Step 2: Book CRUD Routes
-
In
routes/, createbookRoutes.jsand useexpress.Router()to create a router instance. -
Implement the five core CRUD endpoints on this router:
Method Path Behavior POST/Creates a new book from req.body. Responds with the created document and a201status.GET/Retrieves all books and returns them as an array. GET/:idRetrieves a single book by its _id. Responds404if no book has that ID.PUT/:idUpdates a book by its _idusingreq.body. Returns the updated document.DELETE/:idDeletes a book by its _id. Returns a confirmation message. -
Use
async/awaitwithtry...catchblocks in every route, as you practiced in Lesson 4. Send a400status with an error message when validation fails. -
Export the router.
Step 3: The Relationship Endpoint
-
In
routes/, createloanRoutes.js. This endpoint must prove the references in your design actually hold: a loan may only be created for a member and a book that exist.routes/loanRoutes.jsconst express = require('express'); const router = express.Router(); const Loan = require('../models/Loan'); const Member = require('../models/Member'); const Book = require('../models/Book'); router.post('/', async (req, res) => { try { // 1. Look up the member by req.body.member with Member.findById(). // If no member is found, respond 404 with a clear message. // 2. Look up the book by req.body.book with Book.findById(). // If no book is found, respond 404 with a clear message. // 3. Create the loan with Loan.create(req.body). // 4. Respond with the created loan and a 201 status. } catch (error) { res.status(400).json({ message: error.message }); } }); module.exports = router; -
Replace the numbered comments with working code. The skeleton is provided; the logic is yours.
-
Optional stretch: add a
GET /:idroute to this router that uses.populate('member')and.populate('book')so you can see referencing pay off in a response.
Step 4: Server Configuration
- In
server.js, import both routers and mount them: the book router at/api/booksand the loan router at/api/loans(uncomment and complete the two lines from the Part 1 skeleton). - Start the server with
node server.jsand confirm the connection message appears. - Using Postman or Insomnia, create at least one member and one book as test data. Create the book through
POST /api/books; for the member, you may temporarily add a simplePOSTroute or insert a document through Atlas or Compass. Then create a loan throughPOST /api/loansand confirm that a made-up member ID is rejected with a404.
Part 6: Claude Code Architecture Audit (15 minutes)
In a professional setting, “does the code match the approved design?” is a review question, and it is one that AI agents answer well. You will now have Claude Code audit your work.
-
Install Claude Code if you have not already, then start it inside your project directory:
npm install -g @anthropic-ai/claude-code cd digital-bookshelf-api claude -
When prompted, log in with your Per Scholas-provided Claude account (
/login). You may also use the Claude for VS Code extension recommended in Lesson 3; the terminal flow shown here is the canonical one. -
Give Claude Code this prompt:
Read ARCHITECTURE.md and find the section titled "Approved Final Design". Compare every schema in the models/ directory against that approved design. Report any deviations as a numbered list: missing fields, missing or incorrect validation, wrong types, wrong or missing references, or fields that were added without approval. Do not edit any files; report only. -
Claude Code asks permission before editing files or running commands. This session is read-and-report only: if it requests permission to edit a file, decline. Read each permission request deliberately before responding; that habit is part of what this course trains.
-
Fix any reported deviations yourself, in your own editor, then rerun the audit until the report comes back clean. If the audit flags something you believe is correct, verify against
ARCHITECTURE.mdbefore dismissing it; auditors, human and AI alike, are sometimes wrong, and the approved design is the arbiter.
If you do not have access to Claude Code, paste the contents of your three model files and the “Approved Final Design” section of ARCHITECTURE.md into a claude.ai chat and request the same numbered deviation report.
Wrap-Up and Verification (5 minutes)
- Confirm the application runs without errors using
node server.js. - Test all five book endpoints and
POST /api/loanswith Postman or Insomnia, including the404cases for missing books, members, and IDs. - Confirm
ARCHITECTURE.mdcontains all four artifacts: the exact prompts used, the initial AI design, at least three numbered change requests, and the approved final design. - Commit your work and push it to a GitHub repository. Do not include your
.envfile or thenode_modulesdirectory.
Submission Guidelines
Submit a link to your GitHub repository via Canvas. The repository must contain:
- The working Express/Mongoose project:
server.js,db/connection.js,models/Book.js,models/Member.js,models/Loan.js,routes/bookRoutes.js, androutes/loanRoutes.js. ARCHITECTURE.mdwith the prompts used, the initial AI design, your numbered Product Manager change requests, and the approved final design.- A
.gitignorethat excludes.envandnode_modules/.
This lab is graded with the rubric below (50 points total).
Grading Rubric
| Criteria | Description | Points |
|---|---|---|
| Project Setup & Database Connection (10 points) | ||
| File Structure & Dependencies | Project has the correct modular structure (db/, models/, routes/), dependencies are installed, and .gitignore excludes .env and node_modules/. | 5 |
| Database Connection | db/connection.js successfully connects to MongoDB Atlas through Mongoose using the URI from .env. | 5 |
| Architecture Review Artifacts (15 points) | ||
| Initial AI Design & Prompts | ARCHITECTURE.md records the exact prompts used and Claude’s complete first-draft design. | 5 |
| Product Manager Change Requests | At least three substantive, numbered change requests, each justified against a specific business requirement. | 5 |
| Approved Final Design | A revised design is recorded that resolves the change requests and was explicitly approved by the learner. | 5 |
| Schemas & Models (10 points) | ||
| Model Implementation | Book.js, Member.js, and Loan.js match the approved design, with correct types, validation, defaults, and enums. | 5 |
| Relationships | Loan references Member and Book using ObjectId refs as specified in the approved design. | 5 |
| API Endpoints (15 points) | ||
| Book CRUD Routes | All five endpoints work correctly with async/await and try...catch error handling, mounted at /api/books. | 10 |
POST /api/loans | Verifies that the referenced member and book exist (responding 404 if either is missing) before creating the loan; responds 201 with the created loan. | 5 |
| Total | 50 |
Reflection Questions
- Identify one place where the AI Architect’s first draft would have caused a problem in production, and explain how your Product Manager review caught it. If the first draft was sound, identify the flaw you consider most likely in AI-generated data models and how you checked for it.
- How would your embedding versus referencing decisions change if this library were a city system with millions of loans per year? Which parts of the approved design would survive that scale, and which would not?
- Which parts of schema design in this lab still required human judgment that the AI could not supply on its own? Consider the borrowing limit, the requirements the director never wrote down, and the approval decision itself.