Skip to content

Architecture Overview

BlazorForge follows a four-layer architecture. Each layer has a single responsibility and communicates with the others through well-defined interfaces.

This page explains each layer and how they interact.

The content layer holds all the text, images, and configuration for the site. It lives in two folders.

The wwwroot/content/ folder contains Markdown files. Each file has YAML frontmatter at the top and Markdown body below. Journal articles, service pages, and static pages are all Markdown files.

The wwwroot/data/ folder contains JSON files. Site identity, SEO defaults, navigation, footer, and widgets are JSON files.

Nothing in the content layer requires C#. Any text editor can modify it. Any version control system can track it.

The compile layer is a .NET console application in the Tools/ folder. It converts the Markdown files into JSON metadata that the Blazor app reads at runtime.

The compiler does four things.

First, it reads every Markdown file in wwwroot/content/. It splits the frontmatter from the body. It parses the YAML frontmatter into a dictionary. It converts the Markdown body into HTML using the Markdig library.

Second, it writes a JSON file for each Markdown file. Journal articles are collected into a single journal.json array. Service pages are collected into a single services.json array. Static pages are written one file per page.

Third, it adds previous and next navigation to each journal article and service page.

Fourth, it generates sitemap.xml, feed.xml, and robots.txt in wwwroot/.

The compiler runs automatically before every build through an MSBuild target in BlazorForge.csproj.

The application layer is the Blazor WebAssembly app. It renders pages in the browser.

The Pages/ folder contains routable components. Each component has a @page directive that defines its route. There are seven pages: Index, About, Services, ServiceDetail, Journal, JournalDetail, and Contact.

The Shared/ folder contains reusable components. The header, footer, and SEO head are here, along with smaller components like PostCard, ServiceCard, Pagination, and TagList.

The Layout/ folder contains the main layout. This wraps every page with the header, footer, and global structure.

The App.razor file is the root component. It configures the router and handles 404 pages.

The _Imports.razor file declares global using statements for all Razor components.

The service layer contains the C# classes that load data and orchestrate the app. They live in the Services/ folder.

ConfigService loads the five JSON config files from wwwroot/data/. It caches them in memory so subsequent reads are instant.

MarkdownService loads the compiled metadata JSON files from wwwroot/metadata/. It caches them the same way. It also provides methods to find a journal article or service page by slug.

SeoService generates all meta tags, Open Graph tags, Twitter Card tags, and JSON-LD structured data. It exposes a Set method that pages call to provide title, description, and other values. It falls back to site defaults when a value is missing.

BlazorForge uses .NET dependency injection. The services are registered in Program.cs.

builder.Services.AddScoped(sp => new HttpClient
{
BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)
});
builder.Services.AddScoped<ConfigService>();
builder.Services.AddScoped<SeoService>();
builder.Services.AddScoped<MarkdownService>();

All three services are scoped. They are created once per user session and shared across components.

HttpClient is registered as scoped because ConfigService and MarkdownService depend on it.

When a user navigates to a page, the following sequence happens.

The Blazor router in App.razor matches the URL to a page component.

The page component’s OnInitializedAsync method runs.

The method calls MarkdownService to load the page data.

MarkdownService reads the corresponding metadata JSON file from wwwroot/metadata/. If the file was already loaded, it returns the cached copy.

The page component calls SeoService.Set with the title, description, and other values.

SeoService merges the values with site defaults from wwwroot/data/seo.json. It stores the result for the next render.

The page component renders the UI using the loaded data.

SeoHead reads from SeoService and injects the meta tags into the document head.

MainLayout.razor wraps every page. It has three responsibilities.

First, it loads the site configuration through ConfigService. This ensures the config is available before any page renders.

Second, it renders the header and footer around the page content.

Third, it includes SeoHead so meta tags are injected on every page.

App.razor is the root component. It configures the router.

<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(Layout.MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<NotFound>
<PageTitle>Not Found | BlazorForge</PageTitle>
<LayoutView Layout="@typeof(Layout.MainLayout)">
<div class="container section">
<h1>Page not found</h1>
<p>The page you requested could not be found.</p>
<a href="/" class="btn btn-primary btn-icon-arrow">Back to Home</a>
</div>
</LayoutView>
</NotFound>
</Router>

FocusOnNavigate moves keyboard focus to the h1 element after each navigation. This is required for accessibility.

When you run dotnet run, the following build steps happen in order.

The MSBuild target CompileMarkdownContent runs before the build. It executes the Tools project, which compiles the Markdown to JSON.

The Blazor project builds. All Razor components and C# code compile into WebAssembly.

The development server starts on port 5052.

When you publish with dotnet publish -c Release, the same steps run, but the output is optimized. The publish folder contains everything needed for deployment.

Everything in wwwroot/ is served as a static file. The important folders are:

wwwroot/admin/ contains Decap CMS.

wwwroot/content/ contains the source Markdown files. These are not served to the browser in a production build; they are used only by the compiler.

wwwroot/css/ contains the global stylesheet.

wwwroot/data/ contains the JSON config files.

wwwroot/images/ contains images.

wwwroot/js/ contains a small global JavaScript file.

wwwroot/metadata/ contains the compiled metadata. It is generated by the compiler and can be safely deleted and regenerated.