Tuesday, September 23, 2025

Complete Guide to ReactJS with .NET Core and CRUD Example

 1. What is ReactJS?

ReactJS is a popular JavaScript library developed by Facebook for building fast, dynamic, and scalable frontend user interfaces (UI).

  • It follows a component-based architecture.

  • Uses Virtual DOM for high performance rendering.

  • Supports declarative programming and reusable components.

  • Works well with REST APIs or GraphQL for backend integration.

👉 SEO Keywords: ReactJS in .NET Core, React Frontend Development, ReactJS Tutorial, React CRUD with Web API.


2. Installing ReactJS in a .NET Core Application

When building apps in .NET Core + ReactJS, you can set up in two ways:

Option A: Create a React Template with .NET Core

dotnet new react -o MyReactApp cd MyReactApp dotnet run

This scaffolds a .NET Core backend with a React frontend inside ClientApp.

Option B: Add ReactJS to an existing .NET Core project

  1. Go to your project root.

  2. Create React app:

    npx create-react-app ClientApp
  3. Install dependencies:

    cd ClientApp npm install
  4. Configure Startup.cs (or Program.cs in .NET 6+) to serve React app.

    app.UseSpa(spa => { spa.Options.SourcePath = "ClientApp"; if (env.IsDevelopment()) { spa.UseReactDevelopmentServer(npmScript: "start"); } });

3. How ReactJS Supports Frontend UI Development

ReactJS is frontend-only but it integrates seamlessly with backends like .NET Web API.

  • Reusable Components → Build buttons, forms, tables once and reuse.

  • State Management → Manage UI data using hooks (useState, useReducer).

  • Routingreact-router-dom handles single-page app navigation.

  • Data Fetching → Fetch data from .NET Web API using fetch or axios.

  • Cross-platform UI → Works for web, mobile (React Native), and desktop (Electron).


4. Overview: Learn ReactJS from Scratch

🔑 Core Concepts to Master

  1. JSX (JavaScript XML) → Write HTML inside JS.

  2. Components → Class and Functional components.

  3. Props → Pass data between components.

  4. State → Store component data.

  5. Lifecycle Methods & HooksuseEffect, useState.

  6. Routing → Navigation between pages.

  7. Forms & Validation → Controlled and uncontrolled components.

  8. API Integration → Connect with .NET Core Web API.

👉 Start with functional components + hooks (modern standard).


5. Main Components in ReactJS

React apps are built using three main types of components:

  1. Functional Components – Simplest, uses hooks.

    function Welcome(props) { return <h1>Hello, {props.name}</h1>; }
  2. Class Components – Older style, supports lifecycle methods.

    class Welcome extends React.Component { render() { return <h1>Hello, {this.props.name}</h1>; } }
  3. Higher-Order Components (HOC) – Wraps another component to add features.


6. CRUD Example (ReactJS + .NET Web API)

Step 1: Create .NET Web API (Employee Example)

// Employee.cs (Model) public class Employee { public int Id { get; set; } public string Name { get; set; } public string Department { get; set; } }
// EmployeesController.cs [ApiController] [Route("api/[controller]")] public class EmployeesController : ControllerBase { private static List<Employee> employees = new List<Employee> { new Employee { Id = 1, Name = "John", Department = "IT" }, new Employee { Id = 2, Name = "Sara", Department = "HR" } }; [HttpGet] public IActionResult Get() => Ok(employees); [HttpGet("{id}")] public IActionResult Get(int id) => Ok(employees.FirstOrDefault(e => e.Id == id)); [HttpPost] public IActionResult Post(Employee emp) { emp.Id = employees.Max(e => e.Id) + 1; employees.Add(emp); return Ok(emp); } [HttpPut("{id}")] public IActionResult Put(int id, Employee emp) { var employee = employees.FirstOrDefault(e => e.Id == id); if (employee == null) return NotFound(); employee.Name = emp.Name; employee.Department = emp.Department; return Ok(employee); } [HttpDelete("{id}")] public IActionResult Delete(int id) { var emp = employees.FirstOrDefault(e => e.Id == id); if (emp == null) return NotFound(); employees.Remove(emp); return Ok(); } }

Step 2: ReactJS Frontend (CRUD UI)

import React, { useState, useEffect } from "react"; import axios from "axios"; function Employees() { const [employees, setEmployees] = useState([]); const [newEmp, setNewEmp] = useState({ name: "", department: "" }); useEffect(() => { axios.get("/api/employees").then(res => setEmployees(res.data)); }, []); const addEmployee = () => { axios.post("/api/employees", newEmp).then(res => { setEmployees([...employees, res.data]); setNewEmp({ name: "", department: "" }); }); }; const deleteEmployee = (id) => { axios.delete(`/api/employees/${id}`).then(() => { setEmployees(employees.filter(emp => emp.id !== id)); }); }; return ( <div> <h2>Employee List</h2> <ul> {employees.map(emp => ( <li key={emp.id}> {emp.name} - {emp.department} <button onClick={() => deleteEmployee(emp.id)}>Delete</button> </li> ))} </ul> <h3>Add Employee</h3> <input placeholder="Name" value={newEmp.name} onChange={e => setNewEmp({ ...newEmp, name: e.target.value })} /> <input placeholder="Department" value={newEmp.department} onChange={e => setNewEmp({ ...newEmp, department: e.target.value })} /> <button onClick={addEmployee}>Add</button> </div> ); } export default Employees;

✅ With this setup:

  • .NET Web API → Handles backend CRUD operations.

  • ReactJS → Provides UI with API integration.


⚡ Would you like me to also create a step-by-step folder structure (Backend + Frontend) so you can directly scaffold this in your project?

You said:
yes
ChatGPT said:

Perfect 👍 Let’s set up a step-by-step folder structure for a React + .NET Core CRUD project so you can scaffold it directly and start coding without confusion.


📂 Folder Structure: ReactJS + .NET Core CRUD Application

We’ll call the solution EmployeeManagementApp.


1. Backend (.NET Core Web API)

📁 EmployeeManagementApp

EmployeeManagementApp/ │── EmployeeManagementApp.sln # Solution file │ ├── EmployeeManagementApp.API/ # .NET Core Web API Project │ │── Program.cs # Entry point │ │── Startup.cs (if .NET 5) # Middleware & services │ │── appsettings.json # Configurations │ │ │ ├── Controllers/ │ │ └── EmployeesController.cs # CRUD API │ │ │ ├── Models/ │ │ └── Employee.cs # Employee model │ │ │ ├── Data/ (Optional if using EF Core) │ │ └── AppDbContext.cs # DbContext for EF Core │ │ │ └── Properties/ │ └── launchSettings.json # Local server settings

👉 If you’re using Entity Framework Core, you’d add a Migrations/ folder here after running dotnet ef migrations add InitialCreate.


2. Frontend (ReactJS App)

📁 ClientApp (inside the root solution folder)

ClientApp/ │── package.json # React dependencies │── public/ # Static files (index.html, favicon) │── src/ # React source code │ │── index.js # App entry point │ │── App.js # Root component │ │── services/ │ │ └── employeeService.js # Axios API calls │ │ │ ├── components/ # React UI Components │ │ ├── EmployeeList.js # List employees │ │ ├── EmployeeForm.js # Add/Edit employee │ │ └── Navbar.js # Navigation bar (optional) │ │ │ ├── pages/ # Page-level components │ │ ├── HomePage.js │ │ └── EmployeePage.js │ │ │ └── styles/ # CSS or Tailwind setup │ └── App.css

3. Integration Setup

✅ In Program.cs (or Startup.cs for .NET 5):

app.UseSpa(spa => { spa.Options.SourcePath = "ClientApp"; if (env.IsDevelopment()) { spa.UseReactDevelopmentServer(npmScript: "start"); } });

✅ In React (employeeService.js):

import axios from "axios"; const API_URL = "/api/employees"; export const getEmployees = () => axios.get(API_URL); export const addEmployee = (emp) => axios.post(API_URL, emp); export const deleteEmployee = (id) => axios.delete(`${API_URL}/${id}`); export const updateEmployee = (id, emp) => axios.put(`${API_URL}/${id}`, emp);

4. Project Execution

🔹 Run Backend (.NET Core Web API)

cd EmployeeManagementApp.API dotnet run

🔹 Run Frontend (React)

cd ClientApp npm start

The React app will connect to .NET Core Web API at /api/employees.


5. Workflow

  1. Open Solution in Visual Studio / VS Code.

  2. Backend (API) → Handles data storage (SQL Server / In-Memory).

  3. Frontend (React) → Calls backend APIs for CRUD operations.

  4. Shared Development → API runs on https://localhost:5001, React runs on http://localhost:3000.

Sunday, September 21, 2025

CQRS (Command Query Responsibility Segregation): Step-by-step Guide + Real-World .NET Example

 Introduction

CQRS (Command Query Responsibility Segregation) is a software design pattern that separates writes (commands) from reads (queries) into distinct models and often into separate data stores. The result: clearer responsibilities, better scaling for read-heavy systems, and a design that naturally fits event-driven and distributed architectures.

This article explains CQRS step-by-step and provides a concrete, real-world example using .NET Core Web APIs, MediatR style handlers, and a simple Angular client pattern. You’ll get code snippets, design decisions, trade-offs, and SEO-friendly headings to paste directly into your blog.


Why CQRS? Quick benefits

  • Scale reads and writes independently — tune each data store for its primary workload.

  • Optimized read models — denormalized views for fast queries (no costly joins).

  • Separation of concerns — simpler handlers for commands and queries.

  • Fits event-driven systems — easy to publish domain events and react with projections.

  • Enables advanced consistency strategies — eventual consistency, sagas for long-running transactions.

Trade-offs: added complexity, extra infrastructure (read DBs, message bus), eventual consistency challenges.


Step-by-step guide to implement CQRS

Step 1 — Evaluate whether you need CQRS

CQRS is not a silver bullet. Use it when:

  • You have heavy read traffic or complex read queries that hurt write performance.

  • You want to scale reads independently or serve different read models for different clients.

  • You need event-driven integrations or audit/history requirements.

Avoid CQRS for simple CRUD apps or small teams where complexity cost outweighs benefits.

Step 2 — Identify Commands and Queries

  • Commands (intent to change state): PlaceOrder, CancelOrder, UpdateInventory.

  • Queries (read-only): GetOrderSummary, ListProducts, SearchOrders.

Commands are usually POST/PUT operations; queries map to GET endpoints.

Step 3 — Design separate models

  • Write model (domain model) — authoritative, normalized, enforces business rules.

  • Read model (projection/view) — denormalized and optimized for queries (often stored in a different database or a different schema/table).

Step 4 — Implement command handlers

Commands go to handlers that validate, apply business logic, and persist to the write database. These handlers can publish domain events (in-process or to a message bus).

Step 5 — Publish domain events

After the write model changes, publish events (OrderPlaced, InventoryReserved) so projection handlers can update read models. Events should contain required projection data (or an aggregate id + version to fetch if necessary).

Step 6 — Build projections (read model updaters)

Projection handlers consume events and update the read store. This is where eventual consistency appears — the read model is updated after the write completes.

Step 7 — Implement query handlers

Query handlers read from the read model and return data shaped for the UI. Very fast — often a single table or precomputed view.

Step 8 — Handle consistency, retries and idempotency

  • Use idempotent event handlers.

  • Track processed event IDs to avoid double-processing.

  • Expose useful read-after-write guarantees (e.g., query the write DB directly for immediate read on the resource just created, or show a transient UI state).

Step 9 — Monitoring, testing and observability

  • Monitor event lag (time between write commit and projection update).

  • Test command handlers, event publication, and projection handlers separately.

  • Use tracing (for example, attach correlation IDs across command → event → projection flows).

Step 10 — Deploy and scale

  • Scale read DBs horizontally or use read replicas.

  • Command side may require stronger consistency and transactional guarantees.

  • Use message brokers (Kafka, RabbitMQ, Azure Service Bus) when crossing process or machine boundaries.


Real-world example: E‑commerce "Place Order" flow

We’ll show a compact example where placing an order is handled through CQRS. Simplified components:

  • Command: PlaceOrderCommand

  • Command Handler: writes Orders in the write DB and publishes OrderPlacedEvent

  • Projection Handler: consumes OrderPlacedEvent and updates OrderSummary table in the read DB

  • Query: GetOrderSummaryQuery reads from read DB (fast, denormalized)

Note: This example uses in-process publishing (MediatR style). In production you may replace in-process events with a durable message bus to ensure cross-process delivery and reliability.

Data model (simplified)

Write DB (normalized) — tables: Orders, OrderItems, Inventory
Read DB (denormalized) — tables: OrderSummaries (flattened, aggregated data optimized for UI)

SQL: write DB schema (simplified)

CREATE TABLE Orders (
Id UNIQUEIDENTIFIER PRIMARY KEY,
CustomerId UNIQUEIDENTIFIER,
TotalAmount DECIMAL(18,2),
Status VARCHAR(50),
CreatedAt DATETIME2,
RowVersion ROWVERSION
);

CREATE TABLE OrderItems (
Id UNIQUEIDENTIFIER PRIMARY KEY,
OrderId UNIQUEIDENTIFIER FOREIGN KEY REFERENCES Orders(Id),
ProductId UNIQUEIDENTIFIER,
Quantity INT,
UnitPrice DECIMAL(18,2)
);

SQL: read DB schema (denormalized)

CREATE TABLE OrderSummaries (
OrderId UNIQUEIDENTIFIER PRIMARY KEY,
CustomerName NVARCHAR(200),
TotalAmount DECIMAL(18,2),
TotalItems INT,
Status VARCHAR(50),
LastUpdated DATETIME2
);

C# — Command, Handler, Event, Projection (MediatR-style)

// DTOs / Commands
order.TotalAmount = order.Items.Sum(i => i.Quantity * i.UnitPrice);

_writeDb.Orders.Add(order);
await _writeDb.SaveChangesAsync(cancellationToken);

// publish domain event (in-process)
await _mediator.Publish(new OrderPlacedEvent(orderId, request.CustomerId, order.TotalAmount, order.Items.Select(it => new { it.ProductId, it.Quantity })), cancellationToken);

return orderId;
}
}

// Domain Event
public record OrderPlacedEvent(Guid OrderId, Guid CustomerId, decimal TotalAmount, IEnumerable<object> Items) : INotification;

// Projection Handler (updates read DB)
public class OrderPlacedProjectionHandler : INotificationHandler<OrderPlacedEvent>
{
private readonly ReadDbContext _readDb;

public OrderPlacedProjectionHandler(ReadDbContext readDb)
{
_readDb = readDb;
}

public async Task Handle(OrderPlacedEvent notification, CancellationToken cancellationToken)
{
var summary = new OrderSummary
{
OrderId = notification.OrderId,
CustomerName = "(lookup or cached name)",
TotalAmount = notification.TotalAmount,
TotalItems = notification.Items.Sum(x => (int) x.GetType().GetProperty("Quantity")!.GetValue(x)!),
Status = "Placed",
LastUpdated = DateTime.UtcNow
};

_readDb.OrderSummaries.Add(summary);
await _readDb.SaveChangesAsync(cancellationToken);
}
}

// Query + Handler
public record GetOrderSummaryQuery(Guid OrderId) : IRequest<OrderSummaryDto>;

public class GetOrderSummaryQueryHandler : IRequestHandler<GetOrderSummaryQuery, OrderSummaryDto>
{
private readonly ReadDbContext _readDb;

public GetOrderSummaryQueryHandler(ReadDbContext readDb) => _readDb = readDb;

public async Task<OrderSummaryDto> Handle(GetOrderSummaryQuery request, CancellationToken cancellationToken)
{
var row = await _readDb.OrderSummaries.FindAsync(new object[] { request.OrderId }, cancellationToken);
if (row == null) return null;
return new OrderSummaryDto(row.OrderId, row.CustomerName, row.TotalAmount, row.TotalItems, row.Status);
}
}

Tip: Keep events lightweight and include only data needed to update projections. For bigger payloads, consider storing the full aggregate snapshot or giving the projection handler a reliable way to fetch required details.

API endpoints (example)

  • POST /api/commands/place-order — receives PlaceOrderCommand DTO → returns orderId

  • GET /api/queries/order-summary/{orderId} — returns OrderSummaryDto

Angular (very small snippet)

// order.service.ts
placeOrder(payload: PlaceOrderDto) {
return this.http.post<{ orderId: string }>(`/api/commands/place-order`, payload);
}
getOrderSummary(orderId: string) {
return this.http.get<OrderSummaryDto>(`/api/queries/order-summary/${orderId}`);
}

UI flow: after POST returns orderId, call getOrderSummary(orderId). If projection lag is expected, show a transient "processing" state and retry or use WebSocket notifications when projection completes.

Don't Copy

Protected by Copyscape Online Plagiarism Checker

Pages