WEB / WEB-20.01
Monoliths, three-tier architecture, and SPAs
Deployment units, logical roles, and browser rendering are different axes; compare server HTML and SPA data flows for one public note.
1. Core · Mechanism
Read every scenario without motion
Server HTML
- Page intent
The browser prepares to request a public SSR page; the server does not yet know the target.
- Browser frontend
- No document; note intent
- Application server
- No received target
- Local application service
- Service idle
- Local data owner
- Public Tea note available
- Prepare request
The sender prepares its selected GET target; receiver values stay at previous completion during motion.
- Browser frontend
- Prepared GET /server-note
- Application server
- No received target
- Local application service
- Service idle
- Local data owner
- Public Tea note available
- Request arrives
The request reaches the server. The server now knows the GET /server-note target; the service and public note store have not changed.
- Browser frontend
- Prepared GET /server-note
- Application server
- Received GET /server-note
- Local application service
- Service idle
- Local data owner
- Public Tea note available
- Local service read
Server service/data calls are local in the selected process; no physical-tier packet is drawn.
- Browser frontend
- Prepared GET /server-note
- Application server
- Received GET /server-note
- Local application service
- Known public note read
- Local data owner
- Public Tea note available
- Prepare HTML
The server prepares title text in source HTML; the browser has not yet received its document content.
- Browser frontend
- Prepared GET /server-note
- Application server
- Prepared200 HTML Tea note
- Local application service
- Known public note read
- Local data owner
- Public Tea note available
- HTML arrives
The authored snapshot receives a complete response body; parsing and live DOM are separate local stages.
- Browser frontend
- Received200 HTML body
- Application server
- Prepared200 HTML Tea note
- Local application service
- Known public note read
- Local data owner
- Public Tea note available
- Parse/view
The browser displays Tea note from the received HTML. This visible title comes from the server-rendered response.
- Browser frontend
- Projected DOM text: Tea note
- Application server
- Prepared200 HTML Tea note
- Local application service
- Known public note read
- Local data owner
- Public Tea note available
SPA data/view
- Shell completed
This SPA case starts after initial HTML startup with projected Loading; no note API result exists yet.
- Browser frontend
- Startup HTML200 received; projected Loading
- Application server
- No API target received
- Local application service
- Service idle
- Local data owner
- Selected public note store
- Prepare data GET
The frontend prepares a selected API request; initial HTML success does not determine data availability.
- Browser frontend
- Loading; prepared GET /api/notes/tea
- Application server
- No API target received
- Local application service
- Service idle
- Local data owner
- Selected public note store
- API request arrives
The conceptual API target becomes known at server arrival; backend ownership differs from browser intent.
- Browser frontend
- Loading; prepared GET /api/notes/tea
- Application server
- Received GET /api/notes/tea
- Local application service
- Service idle
- Local data owner
- Selected public note store
- Local availability
The selected service determines public-record availability locally; the browser is not the database owner.
- Browser frontend
- Loading; prepared GET /api/notes/tea
- Application server
- Received GET /api/notes/tea
- Local application service
- Known Tea note available
- Local data owner
- Selected public note store
- Prepare JSON
The backend prepares status/media type/body; a prepared response does not give the frontend its new value.
- Browser frontend
- Loading; prepared GET /api/notes/tea
- Application server
- Prepared200 JSON Tea note
- Local application service
- Known Tea note available
- Local data owner
- Selected public note store
- API response arrives
The authored complete-body snapshot arrives; Fetch fulfillment remains distinct from HTTP success.
- Browser frontend
- Received200 Response/body; still Loading
- Application server
- Prepared200 JSON Tea note
- Local application service
- Known Tea note available
- Local data owner
- Selected public note store
- Status and note accepted
The frontend accepts status 200 and the expected note shape. The note is parsed; the DOM update is the next step.
- Browser frontend
- Status/body/schema checked; note parsed
- Application server
- Prepared200 JSON Tea note
- Local application service
- Known Tea note available
- Local data owner
- Selected public note store
- View commit
The frontend projects Tea note into the view after accepting the JSON response. Loading is replaced by the note title.
- Browser frontend
- Projected DOM text: Tea note
- Application server
- Prepared200 JSON Tea note
- Local application service
- Known Tea note available
- Local data owner
- Selected public note store
SPA API404
- Shell completed
This SPA case starts after initial HTML startup with projected Loading; no note API result exists yet.
- Browser frontend
- Startup HTML200 received; projected Loading
- Application server
- No API target received
- Local application service
- Service idle
- Local data owner
- Selected public note store
- Prepare data GET
The frontend prepares a selected API request; initial HTML success does not determine data availability.
- Browser frontend
- Loading; prepared GET /api/notes/missing
- Application server
- No API target received
- Local application service
- Service idle
- Local data owner
- Selected public note store
- API request arrives
The conceptual API target becomes known at server arrival; backend ownership differs from browser intent.
- Browser frontend
- Loading; prepared GET /api/notes/missing
- Application server
- Received GET /api/notes/missing
- Local application service
- Service idle
- Local data owner
- Selected public note store
- Local availability
The selected service determines public-record availability locally; the browser is not the database owner.
- Browser frontend
- Loading; prepared GET /api/notes/missing
- Application server
- Received GET /api/notes/missing
- Local application service
- Known public note missing
- Local data owner
- Selected public note store
- Prepare JSON
The backend prepares status/media type/body; a prepared response does not give the frontend its new value.
- Browser frontend
- Loading; prepared GET /api/notes/missing
- Application server
- Prepared404 JSON not-found
- Local application service
- Known public note missing
- Local data owner
- Selected public note store
- API response arrives
The authored complete-body snapshot arrives; Fetch fulfillment remains distinct from HTTP success.
- Browser frontend
- Received404 Response/body; still Loading
- Application server
- Prepared404 JSON not-found
- Local application service
- Known public note missing
- Local data owner
- Selected public note store
- 404 prevents note update
The frontend receives the 404 Response, but its status check rejects a successful note update. The next step replaces Loading with an error view.
- Browser frontend
- Status guard rejected; no successful note
- Application server
- Prepared404 JSON not-found
- Local application service
- Known public note missing
- Local data owner
- Selected public note store
- Unavailable message shown
The frontend projects Note unavailable into the view. It does not display a new note title as if the API request succeeded.
- Browser frontend
- Projected message: Note unavailable
- Application server
- Prepared404 JSON not-found
- Local application service
- Known public note missing
- Local data owner
- Selected public note store
ACTORS · STATE · DATA FLOW
Deployment units and layers/tiers are different axes
A monolith groups core application behavior in one development/release deployment unit. This does not mean one file or binary: the unit can contain modules, projects, and artifacts, and horizontal replicas can run it in multiple processes or machines. Microsoft .NET examples describe instance core behavior in its own process, not a requirement that the whole topology has one process. Databases and other dependencies may be separate. No framework or deployment changes occur here.
Presentation, application/business, and data access are three logical responsibilities. Layers define code responsibilities and dependencies; tiers define physical deployment boundaries. A three-tier architecture can place these responsibilities in physical tiers, and one tier can contain multiple logical layers. Three boxes do not prove three machines or independently deployed services. Our native fixture keeps server roles in one local builder/service process with a synthetic memory dictionary. Logical roles are visible without claiming a physical cluster.
The frontend renders and handles interaction in a user agent; the backend serves endpoints and owns business/data decisions. Frontend validation or loaded JSON does not replace permission or invariant decisions. One application server can serve both SSR HTML and a JSON API; SPA does not require microservices or separate backend deployment. Monolith, three-tier, and SPA are not mutually exclusive product choices: deployment, placement, and UI behavior answer different questions.
Initial HTML, later data, and DOM are separate states
In server rendering the server reads public Tea note through service/data owners and places content into h1; after HTML arrival the browser can parse/render the DOM. Our selected SPA startup instead returns a note container with Loading text. This is one client-rendered startup, not a rule that every SPA must send empty HTML; server rendering and hydration can coexist. JavaScript can update parts of the initial document without reloading it all.
Startup HTML success is not note API success. The frontend prepares GET /api/notes/tea; route knowledge changes at the server on request arrival. The server prepares JSON, but the frontend does not know the new note until response arrival. Status/headers, body reading/JSON parsing, schema/purpose checks, and DOM update are distinct. Fetch can fulfill with a Response for HTTP errors such as 404; our status guard blocks a successful-note update. Network rejection is a different unmeasured branch.
The scene combines complete body arrival into one step; real Fetch headers can become available before the body is complete. Local status and note-shape checks then precede a plain-text DOM update. Follow what information has actually reached the browser rather than equating receipt of a response with the user seeing the intended result.
2. Red · Trust & Impact
Read every scenario without motion
200, then 404
- The startup document succeeded
The apiFailure branch has startupStatus=200. This is the startup document’s HTTP outcome, not the outcome of the separate missing-note request.
- Document example
- startupStatus=200
- API outcome
- API outcome not inspected yet
- View decision
- Note view pending
- The API request has another outcome
ApiTarget=/api/notes/missing has apiStatus=404. The document’s earlier 200 does not produce a successful note representation for this resource.
- Document example
- startupStatus=200
- API outcome
- apiStatus=404 / not-found
- View decision
- Success gate not met
- What does a fulfilled Response establish?
The Fetch Promise model is a fulfilled Response. An HTTP 404 received this way need not be a network rejection; its HTTP status needs separate evaluation.
- Document example
- startupStatus=200
- API outcome
- fulfilled Response / status=404
- View decision
- HTTP outcome separate
- It was not parsed as a successful note
NoteParsed=false. The application does not consume an error representation under the successful-note contract or use error as title.
- Document example
- startupStatus=200
- API outcome
- body: error=not-found
- View decision
- noteParsed=false
- The view makes its error decision
ProjectedSuccessfulNoteCommitted=false accompanies Note unavailable. Document success and the unsuccessful note-view outcome remain separate.
- Document example
- startupStatus=200
- API outcome
- apiStatus=404
- View decision
- Note unavailable / success=false
- The earlier 200 gives no permission
Receiving a document or using an SPA is not object-authorization evidence. This example compares request outcomes; permission requires the relevant resource’s server decision.
- Document example
- document delivered
- API outcome
- missing resource outcome separate
- View decision
- Startup success != object permission
Source / data
- The SPA source contains Loading
Spa.sourceNoteText=Loading. Inspecting startup source alone does not establish which later data will produce the note.
- Document example
- sourceNoteText=Loading
- API outcome
- Note data not inspected yet
- View decision
- Final title pending
- The note has a separate JSON representation
The API body contains id=tea, title=Tea note and version=1. These are separate input from Loading in startup HTML.
- Document example
- sourceNoteText=Loading
- API outcome
- id=tea / title=Tea note / version=1
- View decision
- View contract pending
- JSON parsing differs from view admission
Parsing the body as JSON does not meet every view condition. The consumer contract establishes the expected title field and resource association.
- Document example
- startup source separate
- API outcome
- JSON representation available
- View decision
- Representation != admitted view
- The title comes from an admitted field
After validation, projectedDomTextAfterValidation=Tea note is selected. It is ordinary for the final view to differ from Loading in startup source.
- Document example
- sourceNoteText=Loading
- API outcome
- title=Tea note
- View decision
- projectedDomText=Tea note
- The server-rendered example starts differently
SSR sourceNoteText=Tea note means the title is already in startup HTML. Equal displayed titles do not establish equal data paths.
- Document example
- SSR sourceNoteText=Tea note
- API outcome
- SPA separate API representation
- View decision
- Same title != same path
- Keep the data source visible
Keep source HTML, API representation and the consumed title separate. SPA or SSR labels do not automatically satisfy permission or safe-consumption conditions.
- Document example
- source and API records separate
- API outcome
- representation owner known
- View decision
- View contract remains explicit
ASSUMPTION · BOUNDARY · CONTROL
An architecture box proves no boundary by itself
Frontend/backend/data boxes do not prove server authorization or network isolation of a data tier. Logical class calls and remote requests differ. Server/service/data fixture calls are local and draw no packets. Browser/server request/response snapshots represent a conceptual HTTP boundary without a native capture claim.
A monolith label does not place every workload in one process, nor does SPA make browser data authoritative. Wrong assumptions can assign validation or persistence to the wrong owner. Only public Tea note exists here; there is no private endpoint or frontend-bypass workflow. Review separates behavior location from code packaging.
HTML success must not hide a later API failure
The SPA shell arrives with Loading. A selected API 404 preserves startup success while making the note result unavailable. A fulfilled promise counter is neither HTTP success nor a parsed note. Without an object matching the note schema, no successful title is projected into the DOM. This is ordinary missing-public-note behavior without real targets or malformed payloads.
An h1 in source HTML and the live DOM are separate observations. A script can change the title, an API failure can leave Loading, and later interaction can change the view again. Read the initial document, received response and DOM update separately. A visible title alone does not establish which backend request produced it.
3. Blue · Secure development & defense
Read every scenario without motion
Source / view
- Separate the two source examples
A local HTML builder and HTMLParser read produce source-text observations; API status/body are the example’s separate representation. These fields are not live browser DOM observations or packet captures. SSR sourceNoteText=Tea note and SPA sourceNoteText=Loading both describe startup documents. A final browser view needs separate evidence.
- Source observation
- SSR: Tea note / SPA: Loading
- Response observation
- API response not inspected yet
- Evidence boundary
- Final browser view unknown
- Correlate the SPA API body
Spa.apiTarget=/api/notes/tea has apiStatus=200. Tea note in its body describes the data resource’s outcome, not by itself application to the DOM.
- Source observation
- SPA startup: Loading
- Response observation
- API 200 / title=Tea note
- Evidence boundary
- DOM application not observed
- Name the modeled view outcome accurately
ProjectedDomTextAfterValidation=Tea note is the example’s expected post-validation view. A native source-parser result is not a measurement of live browser DOM or paint.
- Source observation
- SPA source: Loading
- Response observation
- API title=Tea note
- Evidence boundary
- projectedDomTextAfterValidation=Tea note
- Do not substitute source for final DOM
Loading can remain in source while application code changes the live DOM. Comparing source and final DOM requires the same document and a defined observation time.
- Source observation
- sourceNoteText=Loading
- Response observation
- API result separate
- Evidence boundary
- Source != live DOM observation
- Missing view observation leaves unknown
An API response alone cannot establish the displayed title. Do not borrow another example’s projected view to complete missing browser observation.
- Source observation
- startup/API observations
- Response observation
- view observation missing
- Evidence boundary
- Final live view remains unknown
- Check the dependency sequence
Correlate document, separate API result, consumer validation and view application for the same journey. Success at one stage does not establish the later stages.
- Source observation
- same document context required
- Response observation
- same API resource required
- Evidence boundary
- Each stage needs its own observation
Layer / tier
- Read the role map first
A local HTML builder and HTMLParser read produce source-text observations; API status/body are the example’s separate representation. These fields are not live browser DOM observations or packet captures. Frontend describes browser presentation; backend contains endpoint/render, application service and data-owner responsibilities. These are names of logical roles.
- Source observation
- frontend: browser presentation
- Response observation
- backend: endpoint / service / data
- Evidence boundary
- Deployment layout pending
- A local function call is not a network hop
Roles.serverCalls=in-process means the endpoint invokes the service in the same process. Each layer does not create another HTTP exchange.
- Source observation
- presentation responsibility
- Response observation
- serverCalls=in-process
- Evidence boundary
- Logical boundary != network hop
- A physical tier is another dimension
A layer separates responsibilities and dependencies; a tier describes physical deployment separation. Multiple layers can share a tier.
- Source observation
- logical roles separate
- Response observation
- same process in this fixture
- Evidence boundary
- layersArePhysicalTiers=false
- Monolith does not mandate exactly one process
The artifact records monolithIsOneMandatoryBinaryOrProcess=false. Classifying architecture by process count alone mixes deployment and application organization.
- Source observation
- presentation / backend roles
- Response observation
- physical deployment separate
- Evidence boundary
- monolithIsOneMandatoryBinaryOrProcess=false
- An SPA label does not determine backend topology
SPA describes browser document and navigation behavior. The same frontend can talk to differently deployed backend layers or services; source HTML does not fully expose that layout.
- Source observation
- SPA document behavior
- Response observation
- backend topology not inferred
- Evidence boundary
- Frontend label != full architecture
- Add only known boundaries to the map
This example establishes logical roles and in-process server calls. Cloud tiers, runtime processes and separate service counts require deployment observations.
- Source observation
- roles observed
- Response observation
- in-process calls declared
- Evidence boundary
- Physical layout requires evidence
IMPLEMENTATION · SECURITY CONTROL · VERIFICATION
Keep topology and responsibility ledgers separate
A local HTML builder and HTMLParser read produce source-text observations; API status/body are the example’s separate representation. These fields are not live browser DOM observations or packet captures. Record browser presentation, server endpoints/rendering, application service logic, and data ownership separately. Server calls to service/data are local in one process; no network diagram implies deployment-unit counts. The monolith multiple-project example describes packaging while three-tier guidance addresses physical placement. This lesson performs no framework rewrite, cloud deployment, or performance comparison.
Define method/target/status/media type/schema and the meaning of available results for the frontend/backend contract. A known public note returns title/version; an absent one returns selected404. Backend decisions own availability and the frontend presents results. Independent source HTML and JSON parsing/serialization support the contract, not access-control or deployment-isolation testing.
When the SPA title is plain text, assign the admitted string to the target node’s textContent rather than reparsing the API body with innerHTML. This choice is for an ordinary text node that needs no HTML content; it does not automatically protect URL, attribute or script contexts. In a browser regression, compare node.textContent with the validated title from the same request and verify that an ordinary 404 writes no success title. The lesson’s local HTML parser check does not measure this live DOM step.
Separate arrival, parsing, and UI commit
The scene first prepares sender target intent, changing server target knowledge only at request ARRIVE. During prepared responses the browser keeps Loading or empty knowledge. Response ARRIVE and local status/body/schema steps precede projected DOM text changes. API404 arrives as an ordinary Response, and the local guard prevents note-success commit. Reduced motion preserves the same states and controls.
The SSR source title, SPA Loading source text and API note shape/status are separate observations. Projected DOM text is the selected frontend contract’s expected outcome, not proof of display in a live browser. Establish the real UI result through response receipt, validation and DOM update for the same request.
NOW APPLY IT TO THE CASE
Which evidence separates the startup document from the later note request?
Duru opens the Atlas note card. In the server-rendered example, Tea note is present in the startup HTML. In the SPA example it contains Loading and the note is requested separately from an API. Opening the document does not establish success of that second request.
Which components contribute to the case response? Show where UI, service, platform and external-provider outcomes complete along their respective hops.
Open the related case file ↗READ THE EXAMPLE TOGETHER
Public note source/representation/view ledger
Select a field and examine what it supports and where the evidence ends.
Read the raw artifact and field explanations
{
"roles": {
"frontend": "Browser presentation",
"backend": [
"endpoint/render",
"application service",
"data owner"
],
"serverCalls": "in-process, no fake wire",
"selectedDeployment": "own local fixture, no cloud physical tiers",
"monolithIsOneMandatoryBinaryOrProcess": false,
"layersArePhysicalTiers": false
},
"ssr": {
"target": "/server-note",
"status": 200,
"contentType": "text/html; charset=utf-8",
"sourceHtml": "<!doctype html><html lang=\"en\"><body><h1 id=\"note\">Tea note</h1></body></html>",
"sourceNoteText": "Tea note",
"projectedDomText": "Tea note"
},
"spa": {
"startupTarget": "/spa-note",
"startupStatus": 200,
"sourceHtml": "<!doctype html><html lang=\"en\"><body><h1 id=\"note\">Loading</h1></body></html>",
"sourceNoteText": "Loading",
"apiTarget": "/api/notes/tea",
"apiStatus": 200,
"apiContentType": "application/json",
"body": "{\"id\":\"tea\",\"title\":\"Tea note\",\"version\":1}",
"representation": {
"id": "tea",
"title": "Tea note",
"version": 1
},
"projectedDomTextAfterValidation": "Tea note"
},
"apiFailure": {
"startupStatus": 200,
"apiTarget": "/api/notes/missing",
"apiStatus": 404,
"body": "{\"error\":\"not-found\"}",
"fetchPromiseModel": "fulfilled Response with status404, not network rejection",
"noteParsed": false,
"projectedSuccessfulNoteCommitted": false,
"projectedMessage": "Note unavailable"
},
"limits": {
"native": "own HTML/JSON builders + independent Python HTMLParser only",
"realHttpServerExecuted": false,
"browserFetchExecuted": false,
"browserDomExecuted": false,
"frameworkExecuted": false
},
"selector": {
"language": "XPath",
"xml": "<audit><view apiStatus=\"404\" noteSuccessCommitted=\"false\"/></audit>",
"xpath": "count(/audit/view[@apiStatus='404' and @noteSuccessCommitted='true'])",
"expected": 0,
"positiveFixtureXml": "<audit><view apiStatus=\"404\" noteSuccessCommitted=\"true\"/></audit>",
"positiveExpected": 1,
"scope": "Only authored protocol-decision observations, not product log field names or a live HTTP capture.",
"missingFieldBehavior": "Missing observation is insufficient evidence; zero matches is not proof of correct runtime behavior."
}
}- roles
- Logical responsibilities and physical deployment differ.
- ssr/spa
- Initial source HTML and API representations are separate stages.
- apiFailure
- Startup success differs from data-result failure.
- limits/selector
- Native source-parser versus authored DOM/promise projection scope.
This example compares SSR HTML and SPA JSON flows for a public note. Boxes show responsibilities and arrows show when information arrives; they are not a network capture. The title and error views are selected states rather than a complete framework model.
Two reality checks
Monolith, three-tier, and SPA are mutually exclusive architectures.
Deployment units, physical placement, and UI behavior are different axes that one application can combine.
Initial HTML200 or a fulfilled Fetch proves the note is ready in the DOM.
Startup, status, body/schema, and DOM commit are separate stages; HTTP404 can fulfill with a Response.
YOUR TURN
Work through this story.
Predict the outcome, find the evidence boundary, and explain it in your own words.
SPA startup HTML is 200 but the API returns 404. Is successful-note DOM state committed?
- No; selected noteParsed=false and there is no successful-note commit.
- Yes; HTML 200 makes every subsequent API result successful.
Read the explained answer
No; selected noteParsed=false and there is no successful-note commit. — Startup success is not data availability; the fixture selects a Note unavailable message projection.
Do three backend roles prove three physical hosts?
Selected lesson artifact fields; teaching example
- roles
- {"frontend":"Browser presentation","backend":["endpoint/render","application service","data owner"],"serverCalls":"in-process, no fake wire","selectedDeployment":"own local fixture, no cloud physical tiers","monolithIsOneMandatoryBinaryOrProcess":false,"layersArePhysicalTiers":false}
- Yes; each layer is necessarily a network host.
- No; selected server calls are in-process.
Read the explained answer
No; selected server calls are in-process. — Endpoint/service/data-owner responsibilities may form logical layers; the selected local fixture measures no physical cloud tiers.
SSR source has Tea note and SPA source has Loading, while later projected DOM has Tea note. What does this teach, and which native-browser claim does it not establish?
Read the explained answer
SSR source carries initial note text; SPA applies a validated body to its view after a data request. The artifact uses owned builders/HTMLParser but has browserFetchExecuted/browserDomExecuted=false. The projection supports mechanism reasoning, not actual browser-rendering or framework SSR/hydration execution.
□ I separated source from later view state.
□ I did not count the projection as actual browser/framework execution.
Open the application console · request / response / browser
Also open request, URL, cookie and DOM examples
Sources and scope
- Microsoft Learn — Common web application architecturesWhat is a monolithic application? · What are layers? · Traditional N-Layer architecture applications
- Microsoft Learn — N-tier architecture styleN-tier architecture style · Opening paragraphs: logical layers and physical tiers · Challenges
- MDN — SPA glossarySPA (Single-page application)
- MDN — Using the Fetch APICanceling a request · Handling the response · Checking response status · Reading the response body
- MDN — Node.textContentValue · Differences from innerHTML
- RFC 9110 — HTTP Semantics3.1 Resources · 3.2 Representations · 8.3 Content-Type · 8.6 Content-Length · 9.2.1 Safe Methods · 9.2.2 Idempotent Methods · 9.3.1 GET · 9.3.2 HEAD · 12.5.1 Accept · 15.5.5 404 Not Found · 15.5.6 405 Method Not Allowed · 15.5.7 406 Not Acceptable