Volume 5: Domain Template Creation

Chapter 2: Learning from SchoolCoop — The Reference Implementation

"Before you can design your own domain, you need to understand one completely. Not just what it contains — but why every decision was made the way it was."


Why We Study SchoolCoop

Most technical books teach by showing you simple examples first and building up to complexity. We are going to do the opposite.

SchoolCoop is not a simple example. It is a fully realized, production-grade domain with 15 relational tables, over 350 sample records, and 20 document templates covering the complete operational life of a homeschool co-op organization. It was built with the same care and rigor that you should bring to every domain you create. It is complex because real organizations are complex, and the only way to serve them well is to honor that complexity rather than simplify it away.

By studying SchoolCoop at depth — not skimming it, but truly understanding every table, every relationship, every design choice — you will develop a mental model for domain architecture that you can apply to any industry. Legal services, real estate, healthcare, HR, nonprofit management — they all have the same underlying structure as SchoolCoop. Different entities, different documents, same patterns.

When you finish this chapter, you will be able to look at an unfamiliar organization and immediately see it as a domain waiting to be built. That is the goal.


What SchoolCoop Is

A homeschool cooperative — co-op — is a group of families who pool resources to provide structured education for their children outside the traditional school system. Families pay fees, children enroll in classes taught by parent-volunteers or hired teachers, and the co-op administrator manages the entire operation: registrations, tuition, attendance, communications, field trips, and volunteer coordination.

Before document automation, a co-op administrator spent enormous amounts of time creating documents manually. Class rosters retyped from enrollment spreadsheets. Invoices calculated by hand. Emergency contact cards filled out for each child individually. Progress reports formatted one at a time. Certificates printed and personalized for every student. Permission slips recreated for each field trip.

SchoolCoop eliminates all of that. The administrator maintains her data in CSV files — or eventually in connected live sources — and Data Publisher generates every document she needs in seconds. One administrator, serving 25 students across 19 classes, has her entire document operation automated.

That is the human reality behind the technical architecture. Keep it in mind as we go through the details.


The Entity Map

Every domain begins with entities — the real-world things that the organization tracks. Before you write a single line of JSON or CSV, you need to identify what your domain is actually about.

SchoolCoop tracks seven core entity types:

Organizations — The co-ops themselves. SchoolCoop includes three distinct co-op organizations in its sample data, which allows templates to be generated across multiple organizations without being tied to a single one.

Time — Academic terms. Education is inherently time-structured. Classes happen within terms, fees are assessed per term, and progress is tracked over time. The academic term is the temporal container for everything else.

Families — The household unit. In a co-op, the billing and communication relationship is with the family, not the individual student. A family may have one child or four. Fees, invoices, and parent communications all happen at the family level.

People — Students, parents, and teachers. Three distinct person types with different roles, different data needs, and different document relationships. A student appears on rosters and progress reports. A parent appears on invoices and communications. A teacher appears on class lists and schedules.

Offerings — Classes. What the co-op actually provides. A class has a subject, a teacher, a term, a meeting schedule, and an enrollment limit.

Relationships — Enrollments. The many-to-many relationship between students and classes. A student can be enrolled in multiple classes; a class can have multiple students. The enrollment record is where those relationships are tracked, along with attendance and progress data.

Supporting data — Emergency contacts, medical information, allergies, fees, payments, field trips, and volunteer opportunities. The operational detail that makes the co-op function.

From these seven entity types, SchoolCoop builds 15 CSV tables. Understanding why seven conceptual entities become 15 tables is one of the most important lessons in domain design.


The 15 Tables: Every Decision Explained

coops.csv — The Organizational Anchor

CoopID, CoopName, Address, City, State, ZipCode, Phone, Email, Website,
DirectorName, DirectorEmail, Founded, MaxFamilies, CurrentEnrollment,
AnnualFee, MissionStatement

Why this table exists: Every document that SchoolCoop generates should be branded to the specific organization using it. A family invoice should show the co-op's name and address at the top. A certificate should include the director's name. A welcome packet should reflect the specific co-op's mission.

Without the coops table, every template would need to hardcode organizational information, making the domain impossible to share across organizations.

The design decision: SchoolCoop includes three co-op organizations in its sample data. This is deliberate. Single-record tables are dangerous in sample data because they mask relationship problems. With three organizations, any template that incorrectly fails to filter by organization will generate documents that mix data from multiple co-ops — a bug that becomes immediately visible.

What this teaches you: Every domain should have an organizational anchor table. The entity at the top of your hierarchy — the company, the firm, the practice, the organization — deserves its own table, even if you think users will only ever have one.


academic_terms.csv — Temporal Structure

TermID, CoopID, TermName, StartDate, EndDate, RegistrationDeadline,
TuitionDueDate, Status, Description

Why this table exists: Education is time-bound. A class belongs to a term. A fee is assessed for a term. A progress report covers a term. Without explicit term records, date management becomes brittle — hardcoded in templates or scattered inconsistently across tables.

The design decision: The Status field (Active, Upcoming, Completed) allows templates to filter for the current term without date arithmetic. A class roster can be generated for "active term classes" without the user needing to specify dates.

What this teaches you: Any domain where time periods matter — fiscal years, project phases, billing cycles, seasons — deserves a dedicated time-period table. It is almost always worth creating even when it feels like overhead.


families.csv — The Billing Unit

FamilyID, CoopID, FamilyName, PrimaryAddress, City, State, ZipCode,
PrimaryPhone, PrimaryEmail, SecondaryEmail, JoinDate, MembershipStatus,
Notes, EmergencyNotes

Why this table exists: In a co-op, the family is the contractual relationship. Fees are charged to families, not to individual students. Communications go to families. Emergency notes at the family level apply to all children in that household.

The design decision: SchoolCoop separates families from parents, even though in a two-parent family it might seem redundant. This separation is essential for households with three or four children, households with a single parent, and households where a grandparent is the primary contact. The family record is the billing and communication anchor; the parent records are the individual contacts.

What this teaches you: Look carefully at the difference between the contractual unit and the human unit in your domain. In legal services, the client matters are often billed to a company, not a person. In real estate, transactions involve a household, not an individual. Identifying this distinction early prevents structural problems later.


parents.csv — Individual Contacts

ParentID, FamilyID, FirstName, LastName, Relationship, CellPhone,
WorkPhone, Email, Occupation, VolunteerAvailability, VolunteerSkills,
BackgroundCheckDate, BackgroundCheckStatus

Why this table exists: Each parent is a distinct person with distinct contact information, availability, and skills. Emergency communications may need to reach a specific parent. Volunteer coordination needs to know which parents are available on which days.

The design decision: The VolunteerSkills field anticipates the volunteer coordination use case before it is needed. Good data design looks ahead at how data will flow into documents, not just at what data exists today. A parent's occupation and skills are the raw material for the volunteer schedule template.

What this teaches you: When designing your tables, read through your full list of 20 document types and ask: "what data does each template need?" Then trace that data back to its natural home in the entity structure. This forward-looking design saves costly restructuring later.


students.csv — The Core Subject

StudentID, FamilyID, FirstName, LastName, DateOfBirth, Grade,
Gender, TShirtSize, PhotoPermission, SpecialNeeds, DietaryRestrictions,
PrimaryLanguage, EnrollmentDate, Status

Why this table exists: Students are the primary subjects of most SchoolCoop documents. Rosters, progress reports, attendance sheets, certificates, name tags — all center on the student.

The design decision: Fields like TShirtSize and PhotoPermission might seem granular, but they reflect real operational needs. Field trip name tags need shirt sizes. Event photography requires photo permission tracking. A domain that handles real operations must handle real operational details.

The Status field (Active, Inactive, Withdrawn) means templates can filter for currently active students without deleting historical records — an important distinction when generating end-of-year reports that need to include students who withdrew mid-term.

What this teaches you: Resist the urge to strip your tables down to "just the basics." Real organizations track more than you expect. When in doubt about whether a field belongs, ask: "Is there a document template that would need this data?" If yes, include it.


teachers.csv — The Instructional Staff

TeacherID, CoopID, FirstName, LastName, Email, Phone, Specialty,
Qualifications, Bio, HireDate, Status, VolunteerOrPaid, HourlyRate

Why this table exists: Teachers appear on class lists, meeting schedules, supply lists, and parent communications. They are a distinct entity from parents — a parent can also be a teacher, but the roles carry different data.

The design decision: The VolunteerOrPaid and HourlyRate fields anticipate the financial reporting use case. A co-op that pays some teachers and uses parent volunteers for others needs this distinction when generating financial summaries. Including it in the data structure means financial templates can use it without retrofitting the schema.

What this teaches you: When your domain has people in multiple roles — like parents who are also teachers — give each role its own table with its own data. The relationship between the tables (a parent who is also a teacher) can be captured through a shared email or a cross-reference field, without conflating the two roles into one messy record.


classes.csv — The Core Offering

ClassID, CoopID, TermID, TeacherID, ClassName, Description, Subject,
GradeLevel, DayOfWeek, StartTime, EndTime, Location, MaxStudents,
CurrentEnrollment, SupplyFee, SupplyList, Prerequisites, Status

Why this table exists: Classes are what the co-op sells and what students attend. They are the operational center of the organization — everything else relates to them in some way.

The design decision: The SupplyList field stores a comma-separated list of required materials. This denormalized approach is intentional — the supply list template iterates over this field to generate per-class supply lists, and storing it as a simple text field makes template generation straightforward without requiring a separate class_supplies table.

This is a deliberate trade-off: perfect normalization would suggest a separate table. But for document generation purposes, a flat list in a single field is easier to work with and sufficient for the actual use case.

What this teaches you: Normalization is a goal, not an absolute. When a denormalized field structure makes document generation significantly simpler without compromising data integrity for your actual use cases, the denormalization is justified. Know when to normalize and when to pragmatically simplify.


enrollments.csv — The Relationship Table

EnrollmentID, StudentID, ClassID, TermID, EnrollmentDate, Status,
AttendanceRecord, Grade, ProgressNotes, ParentVolunteerRequired

Why this table exists: The relationship between students and classes is many-to-many — one student can enroll in many classes, and one class can have many students. In relational data design, many-to-many relationships require a junction table. Enrollments is that table.

The design decision: SchoolCoop's enrollment table includes 120 records across 25 students and 19 classes — an average of nearly five classes per student and six students per class. This density is intentional. Sparse junction tables hide relationship problems. Dense ones expose them.

The inclusion of AttendanceRecord, Grade, and ProgressNotes in the enrollment record — rather than in the student record — is critical. A student's grade and progress are specific to a particular class in a particular term. Storing them in the enrollment record correctly captures this specificity.

What this teaches you: Junction tables in document automation are more than relationship records — they are often where the richest per-relationship data lives. In a legal domain, the relationship between a client and a matter carries the matter-specific data. In a real estate domain, the relationship between an agent and a property carries the listing-specific data. Pay close attention to what belongs in your junction tables.


emergency_contacts.csv, medical_info.csv, allergies.csv — The Safety Layer

These three tables form a cluster. Emergency contacts link to students; medical information links to students; allergies link to students (or to the medical record). Together they power the Emergency Contact Card and Medical Alert Sheet templates — the documents that go to field trip chaperones and are kept on file for every student.

The design decision: Separating allergies into their own table rather than storing them as a field in medical_info allows a student to have multiple allergies with different severity levels and treatment protocols. This is not theoretical flexibility — it is a real scenario that affects real children.

What this teaches you: Safety-critical data deserves extra care in its structure. The allergy table might seem like over-engineering until you consider that a template generating a Medical Alert Sheet for a field trip needs to list every allergy a child has, with severity and treatment. That requires iteration over multiple records, not a single text field.


fees.csv and payments.csv — The Financial Layer

FeeID, CoopID, TermID, FeeType, Amount, Description, DueDate, AppliesTo
PaymentID, FamilyID, FeeID, Amount, PaymentDate, PaymentMethod, 
ConfirmationNumber, Notes, RecordedBy

Why these tables exist: The Family Invoice and Payment Receipt templates need financial data structured with enough precision to generate accurate billing statements. A family with three children in different classes has a complex fee structure: per-student enrollment fees, per-class supply fees, and possibly a family membership fee. Payments may be partial or made in installments.

The design decision: Separating fees (what is owed) from payments (what was paid) allows the Invoice template to calculate outstanding balances. This is standard accounting practice, and it applies directly to document generation: a family invoice that shows total charges, payments made, and balance due requires this separation.

What this teaches you: When your domain involves money, model it the way accountants do — not because you are building accounting software, but because accurate financial documents require accurate financial data structure.


field_trips.csv and volunteer_opportunities.csv — The Activity Layer

These two tables handle the operational activities beyond regular classes. Field trips generate Permission Slips and Event Flyers. Volunteer opportunities generate the Volunteer Schedule.

The design decision: Both tables link to classes, not directly to students. A field trip is an event associated with a class; the students who attend are the students enrolled in that class. This indirect relationship through the enrollment junction table is exactly the kind of multi-hop relationship that tests whether your foreign key structure is sound.

What this teaches you: When you design your tables, trace every document template through the full chain of relationships it needs. A permission slip needs student name (students table) → family address (families table) → class name (classes table) → field trip details (field_trips table). Every hop in that chain must be supported by a foreign key relationship.


The Foreign Key Web

The power of SchoolCoop's data structure comes from its foreign key relationships. These are not just database conventions — they are the connective tissue that allows Data Publisher to join data across tables when generating complex documents.

Here is the complete relationship map:

coops ──────────────────── academic_terms
  │                               │
  ├── families ──── parents       │
  │      │                        │
  │      ├── students ────────── enrollments ── classes ── teachers
  │      │      │                                 │
  │      │      ├── emergency_contacts            ├── field_trips
  │      │      ├── medical_info                  └── volunteer_opportunities
  │      │      └── allergies
  │      │
  │      └── payments ── fees
  │
  └── teachers

Every line in this diagram represents a foreign key relationship. When you generate a Family Invoice, Data Publisher follows the path: familiesstudentsenrollmentsclassesfeespayments. Six table joins to produce one document. This works because every relationship in the chain is explicitly defined in domain-config.json.


The 20 Document Templates: Coverage Without Redundancy

Choosing 20 document templates is as much an editorial discipline as a technical one. Twenty templates must cover the complete operational life of the organization without overlapping each other, without leaving obvious gaps, and without including documents that no one would actually use.

SchoolCoop's 20 templates fall into six natural categories:

Operational Documents (Daily Use) These are the documents an administrator generates weekly or monthly. Class Roster, Attendance Sheet, Teacher Class List, and Student Directory. They are simple in structure but high in frequency — a co-op administrator might generate a class roster every week. Fast generation and clean formatting matter more here than complexity.

Family Communications (Regular Touchpoints) Family Invoice, Payment Receipt, Parent Communication, and Enrollment Confirmation. These documents go directly to families and represent the co-op's professional face. They need to be well-formatted, accurate, and branded to the organization.

Safety and Emergency Documents (Critical) Emergency Contact Card and Medical Alert Sheet. These are the most important documents the co-op generates, even if they are generated infrequently. A field trip chaperone carrying the wrong information about a child's allergy is not an inconvenience — it is a safety risk. These templates are designed to be wallet-sized, clearly formatted, and comprehensive.

Student-Centered Documents (Milestone) Progress Report, Certificate, Name Tags, and Birthday Calendar. These documents recognize students as individuals. The Certificate template in particular demonstrates conditional logic — a certificate for academic achievement looks different from one for participation or attendance.

Event and Activity Documents (Periodic) Field Trip Permission Slip, Event Flyer, Supply List, and Welcome Packet. These are generated in response to specific events rather than on a regular schedule. They tend to have more complex layouts and draw from multiple tables simultaneously.

Administrative and Financial Documents (Management) Volunteer Schedule and Financial Summary. These documents are for the administrator rather than for families or students. The Financial Summary especially is a complex document — it aggregates payment data across all families to produce a summary of the co-op's financial position for a given term.

What this categorization reveals: Every organization has these same categories, even if the specific documents differ. Your domain will have daily operational documents, regular communications, safety records, milestone documents, event materials, and administrative summaries. When you are choosing your 20 templates, try to populate each category before adding more to any single one. A domain with 15 operational documents and 5 everything-else documents is unbalanced — it serves one use case deeply and ignores others.


The Sample Data Strategy

SchoolCoop's sample data was built with specific goals in mind. Understanding those goals will help you build better sample data for your own domain.

Goal 1: Geographic and demographic variety The 25 students in SchoolCoop have names drawn from multiple cultural backgrounds. The three co-op organizations are in different states with different addresses. The teachers have varied educational backgrounds. This variety means that a coordinator anywhere can look at the sample data and see something that feels plausible for their community.

Goal 2: Realistic operational scenarios The 120 enrollment records create a realistic distribution: some classes are nearly full, some have room, one is underenrolled. Some students are in five classes; some are in two. Some families have one child enrolled; the Johnson family has four. These variations test every template against real-world edge cases.

Goal 3: Financial complexity Some families have paid in full, some have partial payments, some have outstanding balances. The fee structure includes both per-family membership fees and per-class supply fees. This means the Family Invoice template must handle multiple scenarios — a clean bill, a partial payment situation, an overdue balance — all of which appear in the sample data.

Goal 4: Safety data depth Multiple students have allergies. Several have medical conditions. All have emergency contacts. Some have multiple emergency contacts. The Medical Alert Sheet and Emergency Contact Card templates are genuinely tested against real-complexity data, not just a single clean record.


What the Generator Script Does

Chapter 5 covers the generator script in complete technical detail. But it is worth understanding conceptually here, because the structure of the script reflects the structure of the domain.

The generator script creates all 20 Word templates programmatically using the docx JavaScript library. For each template, it defines the document structure in code: headers, paragraphs, tables, loops, and placeholders. It then runs all 20 template creation functions and writes the resulting .docx files to the word-templates/ directory.

The script for SchoolCoop is over 2,600 lines. That sounds large, but it is almost entirely repetitive structure — the same helper functions (placeholder(), bold(), sectionHeader()) applied to 20 different document layouts. Understanding one template function in the script means understanding all of them.

Here is the key insight: the script is not documentation of the templates; it is the source of truth for the templates. If you need to change a template — update a layout, add a field, modify formatting — you change the script and regenerate. You do not edit the .docx file directly. This discipline is what makes 20 templates maintainable rather than chaotic.


Reading SchoolCoop as Architecture

Now that you understand every component, read SchoolCoop one more time — not as a list of files, but as an architecture.

At the top sits the organizational anchor (coops). Hanging from it is the temporal structure (academic_terms) that gives everything else a time context. Below that are the people — families, parents, students, teachers — each with their own identity and their own relationship to the organization. In the middle, connecting people to offerings, is the junction layer — enrollments binding students to classes. Supporting everything are the operational details: safety data, financial records, activity records.

The 20 document templates are not arbitrary. They systematically serve every stakeholder in the organization: administrators, teachers, parents, and students. They cover every operational phase: registration, ongoing operation, safety, financial management, milestone recognition, and end-of-term summaries.

This is not accidental. This is what good domain design looks like. It starts with the organization's actual operational needs and works backward to the data structure that supports those needs.


The Checklist You Now Carry

After studying SchoolCoop, you have a mental checklist for evaluating any domain you consider building:

Entities: Have you identified every meaningful entity the organization tracks? Did you find all the junction tables hiding inside many-to-many relationships?

Temporal structure: Does your domain have time periods that need their own table? Academic terms, fiscal years, billing cycles, project phases?

Organizational anchor: Is there a top-level entity that everything else hangs from, so the domain can serve multiple organizations from the same template set?

Financial layer: If money changes hands, do you have separate tables for what is owed and what has been paid?

Safety data: If your domain touches people — especially children — have you thought through emergency contacts, medical information, and safety records?

Document coverage: Do your 20 templates serve every stakeholder and every operational phase? Have you balanced the six categories?

Sample data depth: Does your sample data include edge cases, not just ideal scenarios? Multiple children per family, multiple records per entity, varied financial situations?

Generator script: Have you designed your templates with programmatic generation in mind, using consistent placeholder syntax and helper functions?

If you can answer yes to all of these, your domain is ready to build.


Chapter Summary

SchoolCoop is a 15-table, 20-template, 350-record domain serving homeschool co-op administrators. Its design reflects seven years of thinking about how organizations generate documents and what data structures support that generation most effectively.

The key architectural lessons from SchoolCoop are: every domain needs an organizational anchor; temporal structure deserves its own table; many-to-many relationships require junction tables that often carry the richest data; safety-critical data deserves normalized structure even when it feels like over-engineering; financial data should separate what is owed from what is paid; and 20 document templates should systematically cover all stakeholders and all operational phases.

The generator script is the source of truth for all templates. Templates are never edited directly — only regenerated from the script.

Sample data is a strategic asset, not a demo convenience. Rich, realistic, varied sample data is what makes the difference between a domain that feels real and one that feels like a prototype.


What's Next

Chapter 3 is where you start building your own domain. We will walk through the process of identifying the right entities for an unfamiliar domain, mapping real-world workflows to relational data structures, and making the design decisions that SchoolCoop demonstrates. Bring your industry. Bring your domain expertise. The patterns are waiting.


Volume 5 — Building Intelligent Systems: Building Organizational Knowledge Systems on Data Publisher for Word Part of the Building Intelligent Systems Series