Case Study of Architecture & Design Patterns for a Credit Card Banking System

 

Architecture & Design Patterns

This documental post is a sample case study over the architectural considerations, module design, implementation approaches, and database model for a secure and scalable Credit Card Banking Online Website. The focus is on a layered, domain-driven approach, leveraging established design patterns for robustness and maintainability.

1. Overall System Architecture

The proposed architecture adopts a Layered (N-Tier) Architecture as its foundation, which provides clear separation of concerns, simplifies development, and enhances scalability. This structure can evolve towards a microservices approach for specific complex domains (e.g., fraud detection) as the system grows.

Key Architectural Layers:

  1. Presentation Layer (UI/Frontend):

    • Technology: ASP.NET Core (Razor Pages or MVC) for Server-Side Rendering (SSR) and/or a modern JavaScript framework (React, Angular, Vue.js) for a Single Page Application (SPA) communicating with the API. For this document, we'll assume ASP.NET Core Razor Pages for simplicity and full-stack .NET.

    • Functionality: User interface, user interaction, data display, input validation, session management.

  2. API/Application Layer:

    • Technology: ASP.NET Core Web API.

    • Functionality: Exposes endpoints for the UI and potential third-party integrations, handles request validation, orchestrates business logic, and manages data flow between the UI and the domain layer. This layer acts as the entry point for all application functionalities.

  3. Domain/Business Logic Layer:

    • Technology: Plain C# classes, interfaces, and domain models. Independent of any specific framework.

    • Functionality: Contains the core business rules, entities, value objects, and aggregates. This is the heart of the application, ensuring that business rules are enforced consistently regardless of how operations are initiated.

  4. Infrastructure/Data Access Layer:

    • Technology: Entity Framework Core with SQL Server.

    • Functionality: Handles persistence concerns, communicates with the database, implements repositories, and manages Unit of Work. It also includes external service integrations (e.g., SMS gateways, email services).

Supporting Components:

  • Database: SQL Server (relational database for transactional data).

  • Caching: Distributed cache (e.g., Redis) for frequently accessed, less volatile data.

  • Message Broker: (e.g., RabbitMQ, Azure Service Bus) for asynchronous communication, event-driven processes (e.g., fraud alerts, statement generation), and decoupling services.

  • Logging & Monitoring: Centralized logging (e.g., Serilog, ELK Stack), APM tools (e.g., Application Insights) for observability.

  • Security: Identity Management (ASP.NET Core Identity, IdentityServer for OAuth/OpenID Connect).

2. Module Design and Implementation

We will break down the Credit Card Banking website into key functional modules, detailing their design and implementation using appropriate patterns.

2.1. User Authentication & Authorization Module

  • Purpose: Manages user registration, login, password recovery, session management, and role/policy-based access control.

  • Key Components:

    • ApplicationUser: Extends IdentityUser for custom user properties (e.g., FirstName, LastName).

    • ApplicationRole: Extends IdentityRole for custom roles (e.g., CardHolder, Administrator, CustomerService).

    • ApplicationDbContext: Inherits from IdentityDbContext for user/role persistence.

    • AccountController (API) / LoginPage (Razor Page): Handles user registration, login, logout, password resets.

    • JwtService / TokenService: Generates and validates JSON Web Tokens (JWT) for API authentication.

    • CustomAuthorizationHandler: Implements complex authorization policies.

  • Design Patterns:

    • Identity Pattern: Leveraging ASP.NET Core Identity for robust user management.

    • Dependency Injection: Injecting UserManager, SignInManager, RoleManager, and TokenService.

    • Strategy Pattern (for Authentication Schemes): ASP.NET Core handles this internally with schemes (Cookie, JWT Bearer).

  • Implementation Notes:

    • Registration process: User provides details, password hashed via Identity, confirmation email sent (via email service).

    • Login: Validates credentials, issues JWT (for API) or sets a cookie (for web app).

    • Authorization: [Authorize] attribute on controllers/actions for role or policy checks. Policies defined in Startup.cs (AddAuthorization(options => { ... })).

2.2. Account Management Module

  • Purpose: Allows users to view their credit card accounts, current balance, credit limit, available credit, statement date, and minimum payment due. Also handles new card applications.

  • Key Components:

    • Account Entity: Represents a credit card account (e.g., AccountId, UserId, CardNumber, CreditLimit, CurrentBalance, Status).

    • Card Entity: Represents the physical/virtual card details (e.g., CardId, AccountId, ExpiryDate, CVV, PIN, IsActive).

    • AccountService: Business logic for fetching account details, creating new accounts, updating account status.

    • IAccountRepository / AccountRepository: Data access for Account and Card entities.

    • AccountController / AccountDetailsPage: UI/API endpoints for account operations.

  • Design Patterns:

    • Repository Pattern: IAccountRepository abstracts data access from the AccountService.

    • Unit of Work Pattern: DbContext acts as the Unit of Work, ensuring SaveChanges commits all changes atomically.

    • Domain Model: Account and Card are rich domain objects with encapsulated behavior.

  • Implementation Notes:

    • Credit card numbers should be masked/encrypted at rest and in transit.

    • Logic for calculating available credit (CreditLimit - CurrentBalance) resides in the Account domain entity or AccountService.

    • New card applications might involve a workflow and status changes (e.g., "Pending Approval", "Approved", "Declined").

2.3. Transaction Processing Module

  • Purpose: Records all credit card transactions (purchases, refunds, cash advances), applies interest, and manages transaction history.

  • Key Components:

    • Transaction Entity: Represents a single transaction (e.g., TransactionId, AccountId, Amount, Type (Debit/Credit), Description, MerchantName, TransactionDate, Status).

    • TransactionService: Business logic for processing transactions, applying fees, interest calculation, and validation.

    • ITransactionRepository / TransactionRepository: Data access for Transaction entities.

    • TransactionController / TransactionHistoryPage: UI/API endpoints for viewing and managing transactions.

  • Design Patterns:

    • Repository Pattern: ITransactionRepository for data access.

    • Strategy Pattern (for Transaction Types/Fees): Could be used if different transaction types have vastly different processing rules or fee structures.

    • Observer Pattern / Event-Driven: A TransactionProcessedEvent could be published after a transaction is recorded, triggering updates for balance, fraud checks, or statement generation.

  • Implementation Notes:

    • Debit transactions increase CurrentBalance, Credit transactions decrease it.

    • Real-time balance updates are critical.

    • Transaction status (e.g., "Pending", "Posted", "Failed") should be managed.

    • Fraud detection hooks can be integrated here (e.g., a message published to a fraud service).

2.4. Billing & Payments Module

  • Purpose: Generates monthly statements, calculates minimum payments and due dates, and processes user-initiated payments to their credit card.

  • Key Components:

    • Statement Entity: Represents a monthly statement (e.g., StatementId, AccountId, StatementDate, OpeningBalance, ClosingBalance, MinimumPaymentDue, DueDate).

    • Payment Entity: Records payments made by the user (e.g., PaymentId, AccountId, Amount, PaymentDate, PaymentMethod, Status).

    • BillingService: Logic for statement generation (often a scheduled background task), minimum payment calculation, interest accrual.

    • PaymentService: Processes incoming payments, updates account balance, communicates with payment gateways.

    • IStatementRepository / StatementRepository: Data access for Statement entities.

    • IPaymentRepository / PaymentRepository: Data access for Payment entities.

    • BillingController / StatementPage / PaymentPage: UI/API for viewing statements and making payments.

  • Design Patterns:

    • Repository Pattern: For Statement and Payment.

    • Strategy Pattern (for Payment Methods): E.g., ProcessBankTransfer, ProcessDebitCardPayment.

    • Command Pattern: A MakePaymentCommand could encapsulate payment initiation.

    • Scheduler Pattern (via IHostedService): For automated statement generation.

  • Implementation Notes:

    • Payment processing would integrate with a secure payment gateway. Payment details are typically not stored directly in the banking system database (PCI DSS compliance).

    • Statements are generated periodically (e.g., end of month) as a batch process.

    • Payment status needs to be robustly handled (e.g., "Pending", "Success", "Failed", "Refunded").

2.5. Fraud Detection (High-Level)

  • Purpose: Identify and flag suspicious activities.

  • Key Components:

    • FraudDetectionService: Analyzes transaction patterns.

    • FraudAlert Entity: Records detected fraud.

    • Message Queue: Transactions are published to a queue, consumed by a separate fraud detection microservice for asynchronous processing.

  • Design Patterns:

    • Event-Driven Architecture: Transactions raise events that the fraud module consumes.

    • Microservice: Could be a separate, highly scalable service.

2.6. Reporting & Analytics

  • Purpose: Generate various reports for users (e.g., yearly summaries, spending categories) and for internal banking operations.

  • Key Components:

    • ReportService: Business logic for compiling reports.

    • Views/Stored Procedures: Optimized database views or stored procedures for complex reporting queries.

  • Design Patterns:

    • CQRS (Query side): Could be a strong candidate here if reporting requirements become very complex or demand different data models/stores than the transactional system.

    • Data Transfer Objects (DTOs): Used to shape data specifically for reports.

3. SQL Server Database Model

The database design focuses on a relational model, optimized for transactional consistency and integrity.

Core Entities and Tables:

  1. Users Table (for Authentication)

    • UserId (PK, GUID/NVARCHAR) - Maps to AspNetUsers.Id

    • FirstName (NVARCHAR(50))

    • LastName (NVARCHAR(50))

    • DateOfBirth (DATE)

    • PhoneNumber (NVARCHAR(20))

    • Email (NVARCHAR(256), UNIQUE)

    • (Inherits many from AspNetUsers like UserName, PasswordHash, EmailConfirmed, etc.)

  2. Accounts Table

    • AccountId (PK, INT/GUID) - Unique identifier for the credit card account.

    • UserId (FK, GUID/NVARCHAR) - Links to Users.UserId.

    • CardNumber (NVARCHAR(16), UNIQUE) - Masked/Encrypted credit card number (e.g., last 4 digits stored, full encrypted value in vault).

    • CreditLimit (DECIMAL(18, 2)) - Maximum credit allowed.

    • CurrentBalance (DECIMAL(18, 2)) - Current outstanding balance.

    • AvailableCredit (DECIMAL(18, 2)) - Calculated: CreditLimit - CurrentBalance.

    • OpeningDate (DATETIME)

    • Status (NVARCHAR(50)) - e.g., 'Active', 'Closed', 'Frozen', 'Pending'.

    • AnnualInterestRate (DECIMAL(5, 4))

    • MinimumPaymentPercentage (DECIMAL(5, 4))

  3. Cards Table

    • CardId (PK, INT/GUID) - Unique identifier for the physical/virtual card.

    • AccountId (FK, INT/GUID) - Links to Accounts.AccountId.

    • CardHolderName (NVARCHAR(100)) - Name on the card.

    • ExpiryDate (DATE)

    • CVV (NVARCHAR(4)) - Encrypted or only last digits stored.

    • PIN (NVARCHAR(4)) - Encrypted or not stored directly.

    • IssueDate (DATETIME)

    • IsActive (BIT) - Whether the card is currently active.

    • CardType (NVARCHAR(50)) - e.g., 'Visa', 'MasterCard', 'Amex'.

  4. Transactions Table

    • TransactionId (PK, BIGINT)

    • AccountId (FK, INT/GUID) - Links to Accounts.AccountId.

    • Amount (DECIMAL(18, 2))

    • TransactionType (NVARCHAR(20)) - e.g., 'Purchase', 'Refund', 'CashAdvance', 'Payment', 'Interest', 'Fee'.

    • Description (NVARCHAR(255))

    • MerchantName (NVARCHAR(100)) - For purchases.

    • TransactionDate (DATETIME)

    • PostingDate (DATETIME) - When it's officially recorded on the account.

    • Status (NVARCHAR(50)) - e.g., 'Pending', 'Posted', 'Declined', 'Reversed'.

    • ReferenceNumber (NVARCHAR(100), UNIQUE) - External transaction ID.

  5. Statements Table

    • StatementId (PK, INT/GUID)

    • AccountId (FK, INT/GUID) - Links to Accounts.AccountId.

    • StatementDate (DATE)

    • OpeningBalance (DECIMAL(18, 2))

    • ClosingBalance (DECIMAL(18, 2))

    • TotalCredits (DECIMAL(18, 2)) - Sum of all credits in the statement period.

    • TotalDebits (DECIMAL(18, 2)) - Sum of all debits in the statement period.

    • MinimumPaymentDue (DECIMAL(18, 2))

    • DueDate (DATE)

    • IsPaid (BIT) - Whether the minimum payment is met.

  6. Payments Table

    • PaymentId (PK, BIGINT)

    • AccountId (FK, INT/GUID) - Links to Accounts.AccountId.

    • Amount (DECIMAL(18, 2))

    • PaymentDate (DATETIME)

    • PaymentMethod (NVARCHAR(50)) - e.g., 'BankTransfer', 'DebitCard'.

    • Status (NVARCHAR(50)) - e.g., 'Pending', 'Processed', 'Failed', 'Refunded'.

    • ConfirmationCode (NVARCHAR(100), UNIQUE) - From payment gateway.

  7. UserContactInfo (Optional - for multiple contacts per user)

    • ContactId (PK, INT/GUID)

    • UserId (FK, GUID/NVARCHAR)

    • ContactType (NVARCHAR(50)) - e.g., 'Email', 'Phone', 'Address'.

    • ContactValue (NVARCHAR(255))

    • IsPrimary (BIT)

  8. FraudAlerts Table (for Fraud Detection)

    • AlertId (PK, BIGINT)

    • TransactionId (FK, BIGINT, NULLABLE) - Links to a suspicious transaction.

    • AccountId (FK, INT/GUID) - Links to affected account.

    • AlertType (NVARCHAR(50)) - e.g., 'UnusualSpending', 'ForeignTransaction'.

    • AlertDetails (NVARCHAR(MAX))

    • AlertDate (DATETIME)

    • Status (NVARCHAR(50)) - e.g., 'New', 'Investigating', 'Resolved'.

ER Diagram Description:

Given the inability to render a visual ER diagram, I'll describe the relationships:

  • Users (One) to Accounts (Many): One user can have multiple credit card accounts. Accounts.UserId is a Foreign Key referencing Users.UserId.

  • Accounts (One) to Cards (Many): One credit card account can have multiple physical/virtual cards associated with it (e.g., primary and supplementary cards). Cards.AccountId is a Foreign Key referencing Accounts.AccountId.

  • Accounts (One) to Transactions (Many): One account can have many transactions. Transactions.AccountId is a Foreign Key referencing Accounts.AccountId.

  • Accounts (One) to Statements (Many): One account generates multiple statements over time. Statements.AccountId is a Foreign Key referencing Accounts.AccountId.

  • Accounts (One) to Payments (Many): One account can receive many payments. Payments.AccountId is a Foreign Key referencing Accounts.AccountId.

  • Users (One) to UserContactInfo (Many): One user can have multiple contact details. UserContactInfo.UserId is a Foreign Key referencing Users.UserId.

  • Accounts (One) to FraudAlerts (Many): One account can trigger multiple fraud alerts. FraudAlerts.AccountId is a Foreign Key referencing Accounts.AccountId.

  • Transactions (One) to FraudAlerts (Optional One): A fraud alert may be associated with a specific transaction. FraudAlerts.TransactionId is a Foreign Key referencing Transactions.TransactionId, and is nullable as an alert might be account-level.

4. Key Design Patterns Employed

This architecture leverages several fundamental design patterns to achieve maintainability, testability, and scalability.

  1. Model-View-Controller (MVC) / Razor Pages Pattern (Presentation Layer):

    • MVC: Separates application into M (data/logic), V (UI), and C (handling input/orchestrating).

    • Razor Pages: A page-centric approach simplifying UI development with code-behind.

    • Benefit: Clear separation of concerns for the frontend, making development and testing of UI components more manageable.

  2. Repository Pattern (Infrastructure/Data Access Layer):

    • Purpose: Abstracts the data access logic from the business logic. Instead of direct DbContext calls, the business services interact with interfaces like IAccountRepository or ITransactionRepository.

    • Benefit: Decouples the domain layer from the persistence technology (e.g., allows switching from SQL Server to NoSQL with minimal impact on business logic), improves testability (can easily mock repositories).

  3. Unit of Work Pattern (Infrastructure/Data Access Layer):

    • Purpose: Ensures that a series of operations are treated as a single, atomic transaction. In EF Core, the DbContext naturally embodies this pattern by tracking changes and allowing a single SaveChanges() call to commit all changes.

    • Benefit: Maintains data consistency and integrity, simplifies transaction management across multiple repository operations.

  4. Dependency Injection (DI) (Across all Layers):

    • Purpose: A core principle in .NET Core, where components receive their dependencies from an external source (the DI container) rather than creating them.

    • Benefit: Promotes loose coupling, making components independent and easily swappable. Enhances testability by allowing mocking of dependencies.

  5. Strategy Pattern (Domain/Business Logic Layer - e.g., Payments, Interest Calculation):

    • Purpose: Defines a family of algorithms, encapsulates each one, and makes them interchangeable. The strategy lets the algorithm vary independently from clients that use it.

    • Benefit: Useful for scenarios like different payment processing methods (bank transfer, debit card, credit card), different interest calculation methodologies, or varying fraud detection rules. Improves extensibility without modifying core logic.

  6. Observer Pattern (Domain/Application Layer - Event-Driven Communication):

    • Purpose: Defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.

    • Benefit: Enables loose coupling between components that need to react to state changes or events. For instance, when a Transaction is Posted, it could publish a TransactionProcessedEvent that FraudDetectionService or StatementGenerationService observes and reacts to asynchronously via a message queue.

  7. Command Query Responsibility Segregation (CQRS) (Advanced Consideration):

    • Purpose: Separates the read (query) and write (command) models of an application. Commands perform actions, and queries retrieve data. Often implemented with different data stores or optimized paths for each.

    • Benefit: Can provide significant performance and scalability benefits for systems with high read-to-write ratios or complex read models. The query side can be highly optimized (e.g., denormalized views, different database technology) without impacting the transactional write side. Might be considered for reporting and analytics initially, potentially expanding if justified.

This architecture provides a solid, extensible, and maintainable foundation for a Credit Card Banking Online Website, addressing critical concerns like security, scalability, and modularity.

SQL DMV's Guide

SQL Server Dynamic Management Views (DMVs) Guide

Dynamic Management Views (DMVs) are powerful tools in SQL Server that allow database administrators and developers to gain insight into the internal state of the server instance and databases. They provide real-time information about various aspects of SQL Server, including performance, memory usage, I/O operations, query execution, and more.

What are Dynamic Management Views (DMVs)?

DMVs are special views provided by SQL Server that return server state information. They are dynamic because the data they expose reflects the current activity and statistics since the last SQL Server restart or a specific counter reset. This dynamic nature makes them invaluable for:

  • Performance Troubleshooting: Identifying bottlenecks, long-running queries, and resource contention.

  • Monitoring: Tracking server health, resource utilization, and user activity.

  • Diagnosis: Pinpointing specific issues like deadlocks, blocking, and I/O latency.

  • Capacity Planning: Understanding resource consumption trends over time (when combined with historical data collection).

DMVs are typically prefixed with sys.dm_. Some related functions, called Dynamic Management Functions (DMFs), are also available and require parameters (e.g., sys.dm_os_buffer_descriptors (database_id)).

Categories of DMVs

DMVs can be broadly categorized based on the type of information they provide:

  1. Operating System (OS) Related: Information about the SQL Server OS layer, including memory, CPU, I/O, and wait statistics.

  2. Execution Related: Details about currently executing requests, sessions, and connections, as well as cached query plans and statistics.

  3. I/O Related: Statistics on file I/O, disk latency, and buffer pool usage.

  4. Index Related: Information on index usage, missing indexes, and index fragmentation.

  5. Transaction Related: Details about active transactions, locks, and deadlocks.

  6. Database Related: Statistics about database files, space usage, and object-specific information.

  7. Availability Group Related (AlwaysOn AGs): Health and operational state of AlwaysOn Availability Groups.

Key DMVs and Their Purpose

Here's a list of some of the most commonly used and important DMVs, along with their purpose and a basic example query.

1. Operating System (OS) Related DMVs

  • sys.dm_os_wait_stats

    • Purpose: Returns information about all the waits encountered by threads that executed. This is crucial for identifying performance bottlenecks, as high waits in certain categories indicate resource contention (CPU, I/O, memory, locks).

    • Example:

      SELECT
          wait_type,
          waiting_tasks_count,
          wait_time_ms,
          max_wait_time_ms,
          signal_wait_time_ms
      FROM
          sys.dm_os_wait_stats
      ORDER BY
          wait_time_ms DESC;
      
  • sys.dm_os_performance_counters

    • Purpose: Exposes performance counter information maintained by the server. Similar to Windows Performance Monitor counters but accessible directly within SQL Server.

    • Example:

      SELECT
          counter_name,
          cntr_value
      FROM
          sys.dm_os_performance_counters
      WHERE
          counter_name IN ('Batch Requests/sec', 'SQL Compilations/sec', 'SQL Re-Compilations/sec');
      
  • sys.dm_os_latch_stats

    • Purpose: Returns information on all latch waits encountered by threads. Latches are lightweight synchronization objects used by SQL Server to protect in-memory data structures. High latch contention can indicate memory or concurrency issues.

    • Example:

      SELECT
          latch_class,
          wait_time_ms,
          waiting_requests_count
      FROM
          sys.dm_os_latch_stats
      WHERE
          wait_time_ms > 0
      ORDER BY
          wait_time_ms DESC;
      
  • sys.dm_os_sys_info

    • Purpose: Provides a variety of server-level configuration and resource information, such as CPU count, physical memory, and SQL Server process affinity.

    • Example:

      SELECT
          cpu_count,
          physical_memory_kb / 1024 AS physical_memory_mb,
          sql_memory_model_desc
      FROM
          sys.dm_os_sys_info;
      

2. Execution Related DMVs

  • sys.dm_exec_requests

    • Purpose: Returns information about each request that is executing on SQL Server. This is essential for identifying active queries, their status, wait types, and blocking chains.

    • Example:

      SELECT
          session_id,
          status,
          command,
          blocking_session_id,
          wait_type,
          wait_time,
          last_wait_type,
          cpu_time,
          reads,
          writes,
          logical_reads,
          start_time,
          sql_handle,
          statement_start_offset,
          statement_end_offset
      FROM
          sys.dm_exec_requests
      WHERE
          session_id != @@SPID; -- Exclude current session
      
  • sys.dm_exec_sessions

    • Purpose: Returns one row per authenticated session on the SQL Server instance. Provides details like login name, host name, program name, and session status.

    • Example:

      SELECT
          session_id,
          login_name,
          host_name,
          program_name,
          status,
          total_elapsed_time,
          cpu_time,
          memory_usage
      FROM
          sys.dm_exec_sessions
      WHERE
          is_user_process = 1; -- Show user sessions only
      
  • sys.dm_exec_connections

    • Purpose: Returns information about the physical connections established to the SQL Server instance. Useful for seeing connection details like network protocol, authentication method, and last read/write times.

    • Example:

      SELECT
          session_id,
          connect_time,
          net_transport,
          protocol_type,
          client_net_address,
          num_reads,
          num_writes,
          last_read,
          last_write
      FROM
          sys.dm_exec_connections;
      
  • sys.dm_exec_sql_text (sql_handle) (DMF)

    • Purpose: Returns the text of the SQL batch or object given a sql_handle. Often used in conjunction with sys.dm_exec_requests or sys.dm_exec_query_stats to retrieve the actual query text.

    • Example:

      SELECT
          SUBSTRING(st.text, (er.statement_start_offset / 2) + 1,
                    ((CASE er.statement_end_offset
                        WHEN -1 THEN DATALENGTH(st.text)
                        ELSE er.statement_end_offset
                    END - er.statement_start_offset) / 2) + 1) AS statement_text
      FROM
          sys.dm_exec_requests AS er
      CROSS APPLY
          sys.dm_exec_sql_text(er.sql_handle) AS st
      WHERE
          er.session_id = <session_id_from_dm_exec_requests>; -- Replace with an actual session_id
      
  • sys.dm_exec_query_plan (plan_handle) (DMF)

    • Purpose: Returns the showplan XML for the query plan specified by the plan_handle. This is essential for analyzing how a query is executed.

    • Example:

      SELECT
          qp.query_plan
      FROM
          sys.dm_exec_requests AS er
      CROSS APPLY
          sys.dm_exec_query_plan(er.plan_handle) AS qp
      WHERE
          er.session_id = <session_id_from_dm_exec_requests>; -- Replace with an actual session_id
      
  • sys.dm_exec_query_stats

    • Purpose: Returns aggregate performance statistics for cached query plans. This DMV provides insights into frequently executed queries, their average execution times, CPU usage, I/O, etc.

    • Example:

      SELECT TOP 10
          qs.total_worker_time AS total_cpu_time,
          qs.total_elapsed_time,
          qs.total_logical_reads,
          qs.execution_count,
          SUBSTRING(st.text, (qs.statement_start_offset / 2) + 1,
                    ((CASE qs.statement_end_offset
                        WHEN -1 THEN DATALENGTH(st.text)
                        ELSE qs.statement_end_offset
                    END - qs.statement_start_offset) / 2) + 1) AS statement_text,
          qp.query_plan
      FROM
          sys.dm_exec_query_stats AS qs
      CROSS APPLY
          sys.dm_exec_sql_text(qs.sql_handle) AS st
      CROSS APPLY
          sys.dm_exec_query_plan(qs.plan_handle) AS qp
      ORDER BY
          qs.total_worker_time DESC;
      

3. I/O Related DMVs

  • sys.dm_io_virtual_file_stats (database_id, file_id) (DMF)

    • Purpose: Returns I/O statistics for data and log files. Crucial for identifying I/O bottlenecks at the file level.

    • Example:

      SELECT
          DB_NAME(database_id) AS database_name,
          file_id,
          num_reads,
          num_writes,
          io_stall_read_ms,
          io_stall_write_ms,
          io_stall AS total_io_stall_ms,
          size_on_disk_bytes / 1024 / 1024 AS size_on_disk_mb
      FROM
          sys.dm_io_virtual_file_stats(NULL, NULL); -- NULL, NULL for all databases and files
      
  • sys.dm_os_buffer_descriptors

    • Purpose: Returns information about all the data pages currently in the buffer pool. Helps understand memory pressure and which objects are consuming the most memory.

    • Example:

      SELECT
          COUNT(*) AS cached_pages_count,
          obj.name AS object_name,
          idx.name AS index_name,
          SUM(bd.free_space_in_bytes) / 1024.0 / 1024.0 AS total_free_space_mb
      FROM
          sys.dm_os_buffer_descriptors AS bd
      INNER JOIN
          sys.allocation_units AS au ON bd.allocation_unit_id = au.allocation_unit_id
      INNER JOIN
          sys.partitions AS p ON au.container_id = p.hobt_id
      INNER JOIN
          sys.indexes AS idx ON p.index_id = idx.index_id AND p.object_id = idx.object_id
      INNER JOIN
          sys.objects AS obj ON idx.object_id = obj.object_id
      WHERE
          bd.database_id = DB_ID() -- Current database
          AND obj.is_ms_shipped = 0 -- Exclude system objects
      GROUP BY
          obj.name, idx.name
      ORDER BY
          cached_pages_count DESC;
      
  • sys.dm_os_volume_stats (database_id, file_id) (DMF)

    • Purpose: Returns information about the operating system volume (drive) on which the specified database files are stored. Provides details like total space, available space, and logical/physical drive mappings.

    • Example:

      SELECT
          volume_mount_point,
          total_bytes / 1024 / 1024 / 1024 AS total_space_gb,
          available_bytes / 1024 / 1024 / 1024 AS available_space_gb
      FROM
          sys.dm_os_volume_stats(DB_ID(), 1); -- For the current database's primary data file
      

4. Index Related DMVs

  • sys.dm_db_index_usage_stats

    • Purpose: Returns counts of different types of operations performed on indexes (seeks, scans, lookups, updates). Helps identify unused indexes for potential removal or heavily used indexes for optimization.

    • Example:

      SELECT
          OBJECT_NAME(ius.object_id) AS table_name,
          idx.name AS index_name,
          ius.user_seeks,
          ius.user_scans,
          ius.user_lookups,
          ius.user_updates,
          last_user_seek,
          last_user_scan,
          last_user_lookup,
          last_user_update
      FROM
          sys.dm_db_index_usage_stats AS ius
      INNER JOIN
          sys.indexes AS idx ON ius.object_id = idx.object_id AND ius.index_id = idx.index_id
      WHERE
          OBJECTPROPERTY(ius.object_id, 'IsUserTable') = 1 -- Only user tables
          AND ius.database_id = DB_ID()
      ORDER BY
          (ius.user_seeks + ius.user_scans + ius.user_lookups) DESC;
      
  • sys.dm_db_index_physical_stats (database_id, object_id, index_id, partition_number, mode) (DMF)

    • Purpose: Returns size and fragmentation information for the data and indexes of the specified table or view. Essential for index maintenance planning.

    • Example:

      SELECT
          OBJECT_NAME(ips.object_id) AS table_name,
          idx.name AS index_name,
          ips.avg_fragmentation_in_percent,
          ips.page_count,
          ips.record_count
      FROM
          sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'DETAILED') AS ips
      INNER JOIN
          sys.indexes AS idx ON ips.object_id = idx.object_id AND ips.index_id = idx.index_id
      WHERE
          ips.avg_fragmentation_in_percent > 30 -- Highly fragmented
          AND ips.index_id > 0 -- Exclude heaps
          AND OBJECTPROPERTY(ips.object_id, 'IsUserTable') = 1;
      
  • sys.dm_db_missing_index_details

    • Purpose: Returns detailed information about missing indexes that the query optimizer has identified as beneficial.

  • sys.dm_db_missing_index_groups

    • Purpose: Returns information about groups of missing indexes.

  • sys.dm_db_missing_index_columns (index_handle) (DMF)

    • Purpose: Returns information about missing index columns.

    • Example (for missing indexes):

      SELECT
          mid.database_id,
          DB_NAME(mid.database_id) AS database_name,
          mid.object_id,
          OBJECT_NAME(mid.object_id) AS table_name,
          mid.equality_columns,
          mid.inequality_columns,
          mid.included_columns,
          migs.unique_compiles,
          migs.user_seeks,
          migs.user_scans,
          migs.last_user_seek,
          'CREATE INDEX IX_' + OBJECT_NAME(mid.object_id) + '_' + REPLACE(REPLACE(REPLACE(mid.equality_columns, '[', ''), ']', ''), ', ', '_') + ISNULL(REPLACE(REPLACE(REPLACE(mid.inequality_columns, '[', ''), ']', ''), ', ', '_'), '') + ' ON ' + mid.statement + ' (' + mid.equality_columns + ISNULL(', ' + mid.inequality_columns, '') + ')' + ISNULL(' INCLUDE (' + mid.included_columns + ')', '') AS create_index_statement
      FROM
          sys.dm_db_missing_index_groups AS migs
      INNER JOIN
          sys.dm_db_missing_index_details AS mid ON migs.index_handle = mid.index_handle
      WHERE
          mid.database_id = DB_ID()
      ORDER BY
          migs.user_seeks DESC;
      

      Note: The create_index_statement is a simplified example and might need adjustments for complex scenarios.

5. Transaction Related DMVs

  • sys.dm_tran_locks

    • Purpose: Returns information about currently active lock manager resources. Useful for diagnosing blocking and deadlocks.

    • Example:

      SELECT
          request_session_id,
          resource_type,
          resource_database_id,
          DB_NAME(resource_database_id) AS database_name,
          resource_associated_entity_id,
          request_mode,
          request_status,
          request_owner_type
      FROM
          sys.dm_tran_locks
      WHERE
          request_session_id != @@SPID;
      
  • sys.dm_tran_session_transactions

    • Purpose: Returns information about transactions associated with sessions.

    • Example:

      SELECT
          session_id,
          transaction_id,
          is_user_transaction,
          is_local
      FROM
          sys.dm_tran_session_transactions;
      

6. Database Related DMVs

  • sys.dm_db_file_space_usage

    • Purpose: Returns space usage information for each file in the database.

    • Example:

      SELECT
          DB_NAME(database_id) AS database_name,
          file_id,
          total_pages * 8 / 1024 AS total_size_mb,
          used_pages * 8 / 1024 AS used_space_mb,
          unallocated_extent_pages * 8 / 1024 AS unallocated_space_mb
      FROM
          sys.dm_db_file_space_usage;
      
  • sys.dm_db_partition_stats

    • Purpose: Returns page and row count information for each partition in the current database.

    • Example:

      SELECT
          OBJECT_NAME(ps.object_id) AS object_name,
          ps.index_id,
          idx.name AS index_name,
          ps.partition_number,
          ps.in_row_data_page_count,
          ps.row_count,
          ps.reserved_page_count * 8 / 1024 AS reserved_space_mb
      FROM
          sys.dm_db_partition_stats AS ps
      INNER JOIN
          sys.indexes AS idx ON ps.object_id = idx.object_id AND ps.index_id = idx.index_id
      WHERE
          OBJECTPROPERTY(ps.object_id, 'IsUserTable') = 1
      ORDER BY
          reserved_space_mb DESC;
      

7. Availability Group Related DMVs (for AlwaysOn AGs)

  • sys.dm_hadr_database_replica_states

    • Purpose: Returns a row for each database that is participating in an AlwaysOn Availability Group, providing information about its health and synchronization state.

    • Example:

      SELECT
          drs.database_id,
          DB_NAME(drs.database_id) AS database_name,
          hars.replica_server_name,
          drs.synchronization_state_desc,
          drs.suspend_reason_desc,
          drs.redo_queue_size
      FROM
          sys.dm_hadr_database_replica_states AS drs
      INNER JOIN
          sys.dm_hadr_availability_replica_states AS hars ON drs.replica_id = hars.replica_id;
      
  • sys.dm_hadr_availability_replica_states

    • Purpose: Returns a row for each availability replica in an AlwaysOn Availability Group, providing health information for each local and remote replica.

    • Example:

      SELECT
          replica_server_name,
          role_desc,
          connected_state_desc,
          synchronization_health_desc,
          last_connect_error_timestamp
      FROM
          sys.dm_hadr_availability_replica_states;
      

Important Considerations

  • Permissions: To query most DMVs, you need VIEW SERVER STATE permission on the server. For database-specific DMVs, VIEW DATABASE STATE permission on the database is required.

  • Dynamic Data: The data in DMVs is real-time and often cumulative since the last SQL Server service restart. For historical analysis, you need to collect and store this data over time (e.g., using SQL Server Agent jobs, Extended Events, or a monitoring solution).

  • Performance Impact: While DMVs are designed to be efficient, querying very large DMVs frequently, or using complex joins, can have a minor performance impact, especially on heavily loaded servers. Use them judiciously.

  • Documentation: Always refer to the official Microsoft documentation for the most up-to-date and complete information on each DMV and its columns.

By leveraging DMVs, SQL Server professionals can gain deep insights into their database systems, proactively identify and resolve performance issues, and ensure the smooth operation of their critical applications.

SQL Server Dynamic Management Views (DMVs) and Functions (DMFs) Summary

DMV/DMF Name

Purpose

sys.dm_os_wait_stats

Returns information about all the waits encountered by threads, crucial for identifying performance bottlenecks.

sys.dm_os_performance_counters

Exposes performance counter information maintained by the server, similar to Windows Performance Monitor counters.

sys.dm_os_latch_stats

Returns information on all latch waits encountered by threads, indicating memory or concurrency issues.

sys.dm_os_sys_info

Provides server-level configuration and resource information (CPU count, physical memory, SQL Server process affinity).

sys.dm_exec_requests

Returns information about each request currently executing, essential for identifying active queries, status, wait types, and blocking.

sys.dm_exec_sessions

Returns one row per authenticated session, providing details like login name, host name, program name, and session status.

sys.dm_exec_connections

Returns information about physical connections, including network protocol, authentication, and last read/write times.

sys.dm_exec_sql_text (sql_handle)

Returns the text of the SQL batch or object for a given sql_handle.

sys.dm_exec_query_plan (plan_handle)

Returns the showplan XML for a query plan specified by the plan_handle, essential for execution plan analysis.

sys.dm_exec_query_stats

Returns aggregate performance statistics for cached query plans, providing insights into frequently executed queries.

sys.dm_io_virtual_file_stats (database_id, file_id)

Returns I/O statistics for data and log files, crucial for identifying I/O bottlenecks at the file level.

sys.dm_os_buffer_descriptors

Returns information about data pages in the buffer pool, helping understand memory pressure and object memory consumption.

sys.dm_os_volume_stats (database_id, file_id)

Returns information about the operating system volume (drive) where database files are stored, including space and logical/physical mappings.

sys.dm_db_index_usage_stats

Returns counts of operations performed on indexes (seeks, scans, lookups, updates), useful for identifying unused or heavily used indexes.

sys.dm_db_index_physical_stats (database_id, object_id, index_id, partition_number, mode)

Returns size and fragmentation information for data and indexes, essential for index maintenance planning.

sys.dm_db_missing_index_details

Returns detailed information about missing indexes identified by the query optimizer as beneficial.

sys.dm_db_missing_index_groups

Returns information about groups of missing indexes.

sys.dm_db_missing_index_columns (index_handle)

Returns information about specific columns within missing indexes.

sys.dm_tran_locks

Returns information about currently active lock manager resources, useful for diagnosing blocking and deadlocks.

sys.dm_tran_session_transactions

Returns information about transactions associated with sessions.

sys.dm_db_file_space_usage

Returns space usage information for each file in the database.

sys.dm_db_partition_stats

Returns page and row count information for each partition in the current database.

sys.dm_hadr_database_replica_states

Returns health and synchronization state for each database participating in an AlwaysOn Availability Group.

sys.dm_hadr_availability_replica_states

Returns health information for each availability replica (local and remote) in an AlwaysOn Availability Group.

Employee Management System Design - LLD Planning

 This post outlines the comprehensive design for an Employee Management System (EMS), covering software planning, database design, full-stack architectural design, and detailed module functionalities including efforts clocking and leave planning.

Part 1: Introduction & Project Planning

1.1 Project Overview

The Employee Management System (EMS) is a web-based application designed to streamline human resources operations within an organization. It provides a centralized platform for managing employee information, tracking work efforts (clock-in/out), handling leave requests, and generating various HR-related reports. The system aims to improve efficiency, accuracy, and transparency in HR processes.

1.2 Project Objectives

  • Centralize Employee Data: Provide a single source of truth for all employee-related information.

  • Automate Time Tracking: Implement a user-friendly system for employees to clock in and out, and for managers to review time logs.

  • Simplify Leave Management: Facilitate submission, approval, and tracking of various leave types, including balance management.

  • Enhance Reporting: Offer comprehensive reporting capabilities for HR, managers, and employees.

  • Improve Operational Efficiency: Reduce manual HR tasks and minimize errors.

  • Ensure Data Security: Protect sensitive employee data through robust security measures.

1.3 Scope of the System

The initial scope of the EMS includes:

  • Employee Management: Add, view, edit, delete employee profiles.

  • Department & Role Management: Define and manage organizational structure.

  • Time Tracking: Clock-in/out, manual entry (with approval), daily/weekly time logs.

  • Leave Management: Leave type configuration, request submission, approval workflow, balance tracking.

  • User Authentication & Authorization: Secure login, role-based access control (RBAC).

  • Basic Reporting: Time sheets, leave summaries, employee directories.

  • Admin Dashboard: Overview of key HR metrics.

1.4 User Roles and Permissions

The system will support distinct user roles with specific access privileges:

Role

Description

Key Permissions

Employee

Standard organizational member.

View own profile, Clock In/Out, View own time logs, Request leave, View own leave balance/history.

Manager

Oversees a department or team.

All Employee permissions + View team's time logs, Approve/Reject leave requests for direct reports, View team's leave balances.

HR Admin

Manages HR processes and employee data.

All Manager permissions + Add/Edit/Delete employee profiles, Manage departments/roles, Manage leave types, Generate comprehensive reports, Override leave.

System Admin

Manages system configurations and users.

All HR Admin permissions + User account management, Role assignment, System settings.

Part 2: Database Design

The database will be the backbone of the EMS, storing all critical employee, time, and leave data. A relational database (e.g., SQL Server, PostgreSQL, MySQL) is suitable for ensuring data integrity (ACID properties).

2.1 Entity-Relationship (ER) Diagram

+---------------+       +-----------------+       +--------------+
|  Department   |       |      Role       |       |   Employee   |
+---------------+       +-----------------+       +--------------+
| - DeptID (PK) <------>| - RoleID (PK)   |       | - EmployeeID (PK)
| - Name        |       | - Name          |       | - UserID (FK)
| - Description |       | - Description   |       | - DeptID (FK)
+---------------+       +-----------------+       | - RoleID (FK)
                                                  | - ManagerID (FK, self-referencing)
                                                  | - FirstName
                                                  | - LastName
                                                  | - Email (Unique)
                                                  | - PhoneNumber
                                                  | - HireDate
                                                  | - DateOfBirth
                                                  | - Address
                                                  | - IsActive
                                                  | - CreatedAt
                                                  | - LastModifiedAt
                                                  +--------------+
                                                         |
                                                         | One-to-Many
                                                         |
        +-----------------------+     +------------------+     +-------------------+
        |   TimeClockEntry      |     |      Leave       |     |      LeaveType    |
        +-----------------------+     +------------------+     +-------------------+
        | - EntryID (PK)        |     | - LeaveID (PK)   |     | - LeaveTypeID (PK)
        | - EmployeeID (FK)     |     | - EmployeeID (FK)|     | - Name            |
        | - ClockInTime         |     | - LeaveTypeID (FK)|   | - Description     |
        | - ClockOutTime        |     | - StartDate      |     | - MaxDaysPerYear  |
        | - DurationMinutes     |     | - EndDate        |     | - RequiresApproval|
        | - Status (e.g., Approved) |   | - TotalDays      |     +-------------------+
        | - CreatedAt           |     | - Status         |
        +-----------------------+     | - RequestDate    |
                                      | - ApprovalDate   |
                                      | - ApprovedByEmpID (FK) |
                                      | - Reason         |
                                      | - ManagerComment |
                                      +------------------+

Note: For User Authentication, there would be a separate Users table (often managed by ASP.NET Core Identity) which Employee.UserID would link to. This separation allows an employee to have a system login without being directly tied to the employee profile data.

2.2 Schema Details

2.2.1 Departments Table

Column Name

Data Type

Constraints

Description

DeptID

INT

PRIMARY KEY, IDENTITY

Unique identifier for department.

Name

NVARCHAR(100)

NOT NULL, UNIQUE

Department name.

Description

NVARCHAR(255)


Description of the department.

2.2.2 Roles Table

Column Name

Data Type

Constraints

Description

RoleID

INT

PRIMARY KEY, IDENTITY

Unique identifier for role.

Name

NVARCHAR(100)

NOT NULL, UNIQUE

Role name (e.g., "Software Engineer").

Description

NVARCHAR(255)


Description of the role.

2.2.3 Employees Table

Column Name

Data Type

Constraints

Description

EmployeeID

INT

PRIMARY KEY, IDENTITY

Unique identifier for employee.

UserID

NVARCHAR(450)

NOT NULL, UNIQUE, FOREIGN KEY (AspNetUsers.Id)

Links to the user's login account.

DeptID

INT

NOT NULL, FOREIGN KEY (Departments.DeptID)

Department employee belongs to.

RoleId

INT

NOT NULL, FOREIGN KEY (Roles.RoleID)

Role of the employee.

ManagerID

INT

NULLABLE, FOREIGN KEY (Employees.EmployeeID)

Self-referencing to employee's manager.

FirstName

NVARCHAR(50)

NOT NULL

Employee's first name.

LastName

NVARCHAR(50)

NOT NULL

Employee's last name.

Email

NVARCHAR(255)

NOT NULL, UNIQUE

Employee's work email.

PhoneNumber

NVARCHAR(20)

NULLABLE

Employee's phone number.

HireDate

DATE

NOT NULL

Date employee was hired.

DateOfBirth

DATE

NULLABLE

Employee's date of birth.

Address

NVARCHAR(500)

NULLABLE

Employee's address.

IsActive

BIT

NOT NULL, DEFAULT 1

Flag for active/inactive employee.

CreatedAt

DATETIME

NOT NULL, DEFAULT GETDATE()

Record creation timestamp.

LastModifiedAt

DATETIME

NOT NULL, DEFAULT GETDATE()

Last modification timestamp.

2.2.4 LeaveTypes Table

Column Name

Data Type

Constraints

Description

LeaveTypeID

INT

PRIMARY KEY, IDENTITY

Unique ID for leave type.

Name

NVARCHAR(50)

NOT NULL, UNIQUE

Leave type name (e.g., "Annual Leave", "Sick Leave").

Description

NVARCHAR(255)


Description of leave type.

MaxDaysPerYear

INT

NULLABLE

Maximum days allowed per year for this type.

RequiresApproval

BIT

NOT NULL, DEFAULT 1

Does this leave type need manager approval?

2.2.5 Leaves Table

Column Name

Data Type

Constraints

Description

LeaveID

INT

PRIMARY KEY, IDENTITY

Unique ID for leave request.

EmployeeID

INT

NOT NULL, FOREIGN KEY (Employees.EmployeeID)

Employee requesting leave.

LeaveTypeID

INT

NOT NULL, FOREIGN KEY (LeaveTypes.LeaveTypeID)

Type of leave requested.

StartDate

DATE

NOT NULL

Start date of leave.

EndDate

DATE

NOT NULL

End date of leave.

TotalDays

DECIMAL(5,1)

NOT NULL

Total days requested (e.g., 0.5, 1.0).

Status

NVARCHAR(20)

NOT NULL, CHECK ('Pending', 'Approved', 'Rejected', 'Canceled')

Current status of the request.

RequestDate

DATETIME

NOT NULL, DEFAULT GETDATE()

Date leave was requested.

ApprovalDate

DATETIME

NULLABLE

Date leave was approved/rejected.

ApprovedByEmpID

INT

NULLABLE, FOREIGN KEY (Employees.EmployeeID)

Employee who approved/rejected.

Reason

NVARCHAR(MAX)

NOT NULL

Reason for leave request.

ManagerComment

NVARCHAR(MAX)

NULLABLE

Manager's comment on approval/rejection.

2.2.6 TimeClockEntries Table

Column Name

Data Type

Constraints

Description

EntryID

BIGINT

PRIMARY KEY, IDENTITY

Unique ID for time clock entry.

EmployeeID

INT

NOT NULL, FOREIGN KEY (Employees.EmployeeID)

Employee who clocked in/out.

ClockInTime

DATETIME

NOT NULL

Timestamp of clock-in.

ClockOutTime

DATETIME

NULLABLE

Timestamp of clock-out. Null if still clocked in.

DurationMinutes

INT

NULLABLE

Calculated duration of the shift in minutes.

Status

NVARCHAR(50)

NOT NULL, DEFAULT 'Pending', CHECK ('Pending', 'Approved', 'Rejected')

Status of the time entry (e.g., for manual edits).

CreatedAt

DATETIME

NOT NULL, DEFAULT GETDATE()

Timestamp of entry creation.

2.3 Indexing Strategy

  • Foreign Keys: Automatically indexed by most RDBMS.

  • Frequently Queried Columns:

    • Employees: (DeptID), (RoleID), (ManagerID), (IsActive), (LastName, FirstName) for searching.

    • Leaves: (EmployeeID, Status, StartDate, EndDate), (ApprovedByEmpID).

    • TimeClockEntries: (EmployeeID, ClockInTime, ClockOutTime) for efficient time log retrieval.

  • Unique Constraints: Enforce uniqueness where needed (e.g., Email in Employees).

2.4 Relationships

(As illustrated in the ER Diagram: One-to-Many relationships between Department and Employee, Role and Employee, Employee and Leaves, Employee and TimeClockEntries, LeaveType and Leaves. Employee also has a self-referencing relationship for ManagerID.)

Part 3: Software Architecture Design

3.1 Overall Architecture Diagram: Layered with API Backend-for-Frontend (BFF)

The system will adopt a modern, layered architecture with an ASP.NET Core Web API acting as a Backend-for-Frontend (BFF) to serve a Blazor web application. This provides modularity, scalability, and enhanced security.

+--------------------+
| Blazor Web App     |    (Web Browser)
| (Presentation Layer)|
+--------------------+
          | HTTP/HTTPS (RESTful API Calls)
          v
+--------------------+
| ASP.NET Core API   |    (API Layer / BFF)
| (Controllers)      |
+--------------------+
          |
          v
+---------------------+
| Business Logic Layer|    (Services, Validators, Domain Logic)
| (BLL)               |
+---------------------+
          |
          v
+---------------------+
| Data Access Layer   |    (Repositories, ORM - Entity Framework Core)
| (DAL)               |
+---------------------+
          |
          v
+---------------------+
| SQL Server Database |    (Persistent Storage)
+---------------------+

  • Presentation Layer (Blazor): Handles all UI rendering, user interactions, and data binding. Communicates with the API Layer.

  • API Layer (ASP.NET Core Web API): Exposes RESTful endpoints for the frontend. Handles request routing, model binding, and basic validation. Acts as a facade to the BLL.

  • Business Logic Layer (BLL): Encapsulates core business rules, workflows, calculations (e.g., leave day calculations), and orchestrates calls to the DAL. Contains services for employee management, time tracking, leave management, etc.

  • Data Access Layer (DAL): Responsible for abstracting database operations. Uses Entity Framework Core (EF Core) to interact with the SQL Server database. Provides repositories for each entity.

  • Domain Model Layer (Models): Simple C# POCOs (Plain Old CLR Objects) that represent the entities in the database. Shared across DAL, BLL, and often API.

3.2 Technology Stack

  • Backend Platform: .NET 8 (LTS)

    • Web Framework: ASP.NET Core Web API

    • ORM: Entity Framework Core (for database interaction)

    • Authentication/Authorization: ASP.NET Core Identity (with JWT Bearer Tokens)

    • Logging: Serilog (integrated with cloud logging like Application Insights/CloudWatch)

    • Dependency Injection: Built-in .NET Core DI

  • Frontend Platform: .NET 8 (LTS)

    • UI Framework: Blazor WebAssembly (recommended for rich client-side interactivity, offline capabilities, and reduced server load)

    • Charting: A Blazor-compatible charting library (e.g., Syncfusion Blazor Charts, Chart.js via JS Interop)

    • UI Components: MudBlazor, Blazorise, or a custom component library for consistent UI.

  • Database: SQL Server (Azure SQL Database or on-premise)

  • Development Tools: Visual Studio 2022, Visual Studio Code, SQL Server Management Studio.

  • Version Control: Git (e.g., Azure Repos, GitHub).

  • CI/CD: Azure DevOps Pipelines or GitHub Actions.

3.3 Security Considerations

  • Authentication: Secure user login using ASP.NET Core Identity, supporting features like password hashing, MFA (Multi-Factor Authentication). JWT tokens for stateless API authentication.

  • Authorization: Implement Role-Based Access Control (RBAC) at the API endpoint level ([Authorize(Roles = "HR Admin")]) and within Blazor components (e.g., show/hide UI elements).

  • Data Encryption:

    • At Rest: Database encryption (e.g., Transparent Data Encryption in SQL Server).

    • In Transit: All communication over HTTPS/SSL.

  • Input Validation: Robust server-side and client-side validation to prevent injection attacks (SQL injection, XSS) and ensure data integrity.

  • Secure Secrets Management: Store sensitive connection strings, API keys, and other secrets securely using Azure Key Vault or AWS Secrets Manager. Never hardcode secrets.

  • Least Privilege Principle: Database users, application service accounts, and API identities should have only the minimum necessary permissions.

  • Auditing and Logging: Comprehensive logging of security-related events (failed logins, unauthorized access attempts, critical data modifications).

  • Vulnerability Scanning: Integrate security scanning tools into the CI/CD pipeline for code, containers, and infrastructure.

Part 4: Detailed Modules & Features

4.1 Employee Management

  • CRUD Operations: Add new employees, view profiles, edit details (personal, contact, job), deactivate/reactivate employees.

  • Employee Directory: Search and filter employees by department, role, name, status.

  • Profile Viewer: Dedicated page for each employee's detailed profile.

  • Manager Assignment: Assign managers to employees (linking to ManagerID).

  • Role & Department Assignment: Assign employees to departments and roles.

  • Data Validation: Ensure all mandatory fields are filled and data formats are correct (e.g., email format).

4.2 Department & Role Management

  • CRUD Operations: Add, view, edit, delete departments and roles.

  • Hierarchical View: Potentially visualize department hierarchy.

  • Assignment Tracking: View which employees belong to which department/role.

4.3 Efforts Clocking (Time Tracking)

  • Clock In/Out: Simple, intuitive interface for employees to record start and end of their work day.

  • Real-time Status: Display current clock-in/out status.

  • Manual Entry & Adjustments (with Approval):

    • Employees can request manual time entries (e.g., forgot to clock in).

    • Managers/HR can approve/reject manual entries or adjust existing ones.

  • Time Logs:

    • Employee View: Daily/weekly view of own clock entries and total hours.

    • Manager View: View time logs for direct reports.

    • HR View: View time logs for all employees, with filtering.

  • Dashboard Integration: Display current clock-in status on employee dashboard.

  • Reports: Daily/Weekly/Monthly timesheets, overtime reports, attendance reports.

4.4 Leave Planning & Management

  • Leave Type Configuration: HR can configure various leave types (LeaveTypes table) with rules (e.g., "Annual Leave," "Sick Leave," "Bereavement Leave").

  • Leave Request Submission:

    • Employees select leave type, start date, end date, and provide a reason.

    • System calculates TotalDays based on selected dates (considering weekends/holidays if configurable).

  • Approval Workflow:

    • Requests are routed to the employee's assigned ManagerID.

    • Managers receive notifications for pending requests.

    • Managers can view request details, employee's leave balance, and approve/reject with comments.

    • HR Admins can view and manage all leave requests.

  • Leave Balance Tracking:

    • System automatically tracks and displays current leave balances for each employee (per leave type).

    • Accrual rules (if applicable) can be implemented in the BLL.

    • Leave history with statuses for each employee.

  • Calendar View: Optional calendar showing approved leaves across departments/teams.

  • Reports: Leave balance reports, leave history reports, upcoming leave schedules.

4.5 User Authentication & Authorization

  • Login/Logout: Secure login pages.

  • Role Management: System Admin assigns roles to users.

  • Password Management: Secure password reset, strong password policies.

  • User Provisioning: Link system users (AspNetUsers) to Employees.

4.6 Reporting & Analytics

  • Pre-defined Reports: Generate common HR reports (e.g., Employee Roster, Department Headcount, Leave Summaries, Timesheet Exports).

  • Filter & Export: Reports will include filtering options and export capabilities (CSV, PDF).

  • Dashboard Metrics: Key performance indicators (KPIs) like active employees, total leave days, average work hours.

4.7 Admin Dashboard

  • Centralized overview for HR/System Admins.

  • Quick stats on employees, pending leaves, clock-in status.

  • Links to key administration modules.

Part 5: Implementation Plan

The implementation will follow an agile approach, iteratively delivering features in sprints.

Phase 1: Foundation & Core Employee Management (Sprints 1-4)

  1. Environment Setup:

    • Set up .NET 8 SDK, Visual Studio, SQL Server.

    • Initialize Git repository.

    • Set up basic CI/CD for backend API and frontend Blazor app (build, test, deploy to Dev environment).

  2. Database & DAL Setup:

    • Create Departments and Roles tables.

    • Implement AspNetUsers (ASP.NET Core Identity).

    • Create Employees table with initial FKs to Departments, Roles, and AspNetUsers.

    • Develop EF Core DbContext and initial repositories (DepartmentRepository, RoleRepository, EmployeeRepository).

  3. Backend API & BLL:

    • Create ASP.NET Core Web API project.

    • Implement basic authentication (login/logout).

    • Develop BLL services for Employee, Department, Role management.

    • Expose CRUD API endpoints for these entities.

  4. Frontend (Blazor):

    • Create Blazor project.

    • Develop login/logout UI.

    • Create UI for Department and Role management (Admin/HR only).

    • Develop Employee Directory and Employee Profile (view-only initially).

    • Basic RBAC integration (show/hide admin sections).

Phase 2: Time Tracking (Sprints 5-8)

  1. Database & DAL:

    • Create TimeClockEntries table.

    • Extend EmployeeRepository or create TimeClockRepository.

  2. Backend API & BLL:

    • Develop BLL service for time tracking.

    • Expose API endpoints for Clock In/Out, submitting manual entries.

    • Implement logic for calculating DurationMinutes.

  3. Frontend (Blazor):

    • Develop Clock In/Out UI for employees.

    • Implement My Time Logs view for employees.

    • Create Team Time Logs view for managers.

    • Add Timesheet Report for HR.

Phase 3: Leave Management (Sprints 9-12)

  1. Database & DAL:

    • Create LeaveTypes and Leaves tables.

    • Develop LeaveTypeRepository and LeaveRepository.

  2. Backend API & BLL:

    • Develop BLL service for leave management (request, approval, balance calculation).

    • Expose API endpoints for LeaveTypes CRUD.

    • Expose API endpoints for submitting leave requests, approving/rejecting.

  3. Frontend (Blazor):

    • Develop Leave Types configuration UI (HR/Admin).

    • Create Request Leave form for employees.

    • Implement My Leave History/Balance view for employees.

    • Develop Leave Approval Dashboard for managers.

    • Add Leave Summary Reports for HR.

Phase 4: Reporting, Admin & Security Refinement (Sprints 13-16)

  1. Reporting Enhancements:

    • Develop advanced reporting features (e.g., custom filters, advanced exports).

    • Implement specific HR-required reports.

  2. Admin Dashboard:

    • Develop the Admin Dashboard UI with key metrics.

    • Implement User Management UI (linking AspNetUsers to Employees).

  3. Security Hardening:

    • Conduct internal security audits and penetration testing.

    • Implement secure secret management (e.g., Azure Key Vault).

    • Refine authorization policies.

  4. Performance Optimization:

    • Optimize database queries and indexes.

    • Tune API and Blazor performance.

  5. Final Testing & Documentation:

    • Comprehensive UAT (User Acceptance Testing).

    • Create user manuals and technical documentation.

    • Prepare for production deployment.

Part 6: Illustrative C# Code Snippets

These snippets provide a conceptual understanding of the C# implementation across layers.

6.1 Domain Models (Similar to Database Schema)

// Models/Employee.cs
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.AspNetCore.Identity; // For linking to AspNetUsers

namespace EmployeeManagement.Domain.Models
{
    public class Employee
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int EmployeeID { get; set; }

        [Required]
        [StringLength(450)] // Matches AspNetUsers.Id length
        public string UserID { get; set; } // Foreign Key to AspNetUsers.Id

        [Required]
        public int DeptID { get; set; } // Foreign Key

        [Required]
        public int RoleID { get; set; } // Foreign Key

        public int? ManagerID { get; set; } // Self-referencing Foreign Key

        [Required]
        [StringLength(50)]
        public string FirstName { get; set; }

        [Required]
        [StringLength(50)]
        public string LastName { get; set; }

        [Required]
        [StringLength(255)]
        public string Email { get; set; }

        [StringLength(20)]
        public string PhoneNumber { get; set; }

        [Required]
        public DateTime HireDate { get; set; }

        public DateTime? DateOfBirth { get; set; }

        [StringLength(500)]
        public string Address { get; set; }

        [Required]
        public bool IsActive { get; set; } = true;

        public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
        public DateTime LastModifiedAt { get; set; } = DateTime.UtcNow;

        // Navigation Properties
        [ForeignKey("UserID")]
        public IdentityUser User { get; set; } // Link to ASP.NET Core Identity User
        [ForeignKey("DeptID")]
        public Department Department { get; set; }
        [ForeignKey("RoleID")]
        public Role Role { get; set; }
        [ForeignKey("ManagerID")]
        public Employee Manager { get; set; } // Self-referencing navigation
        public ICollection<Employee> DirectReports { get; set; } // For manager's direct reports
        public ICollection<Leave> Leaves { get; set; }
        public ICollection<TimeClockEntry> TimeClockEntries { get; set; }
    }

    // Other models (Department, Role, LeaveType, Leave, TimeClockEntry) similar to DB schema
}


6.2 - 6.2 Data Access Layer (DAL) Example: ApplicationDbContext and EmployeeRepository

EMS Data Access Layer (DAL) 

// Data/ApplicationDbContext.cs
using EmployeeManagement.Domain.Models;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;

namespace EmployeeManagement.Infrastructure.Data
{
    public class ApplicationDbContext : IdentityDbContext
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
        {
        }

        public DbSet<Employee> Employees { get; set; }
        public DbSet<Department> Departments { get; set; }
        public DbSet<Role> Roles { get; set; }
        public DbSet<LeaveType> LeaveTypes { get; set; }
        public DbSet<Leave> Leaves { get; set; }
        public DbSet<TimeClockEntry> TimeClockEntries { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder); // Important for IdentityDbContext

            // Configure Employee self-referencing relationship
            modelBuilder.Entity<Employee>()
                .HasOne(e => e.Manager)
                .WithMany(e => e.DirectReports)
                .HasForeignKey(e => e.ManagerID)
                .IsRequired(false); // ManagerID can be null for top-level employees

            // Configure Leave status enum mapping
            modelBuilder.Entity<Leave>()
                .Property(l => l.Status)
                .HasConversion<string>(); // Store enum as string in DB

            // Configure TimeClockEntry status enum mapping
            modelBuilder.Entity<TimeClockEntry>()
                .Property(t => t.Status)
                .HasConversion<string>(); // Store enum as string in DB
        }
    }
}

// Data/Repositories/IEmployeeRepository.cs
using EmployeeManagement.Domain.Models;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace EmployeeManagement.Infrastructure.Repositories
{
    public interface IEmployeeRepository : IGenericRepository<Employee>
    {
        Task<Employee> GetEmployeeByUserIdAsync(string userId);
        Task<IEnumerable<Employee>> GetEmployeesByDepartmentAsync(int departmentId);
        Task<IEnumerable<Employee>> GetDirectReportsAsync(int managerId);
        // ... more specific employee queries
    }
}

// Data/Repositories/EmployeeRepository.cs
using EmployeeManagement.Domain.Models;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace EmployeeManagement.Infrastructure.Repositories
{
    public class EmployeeRepository : GenericRepository<Employee>, IEmployeeRepository
    {
        private readonly ApplicationDbContext _context;

        public EmployeeRepository(ApplicationDbContext context) : base(context)
        {
            _context = context;
        }

        public async Task<Employee> GetEmployeeByUserIdAsync(string userId)
        {
            return await _context.Employees
                                 .Include(e => e.Department)
                                 .Include(e => e.Role)
                                 .FirstOrDefaultAsync(e => e.UserID == userId);
        }

        public async Task<IEnumerable<Employee>> GetEmployeesByDepartmentAsync(int departmentId)
        {
            return await _context.Employees
                                 .Where(e => e.DeptID == departmentId)
                                 .Include(e => e.Role)
                                 .ToListAsync();
        }

        public async Task<IEnumerable<Employee>> GetDirectReportsAsync(int managerId)
        {
            return await _context.Employees
                                 .Where(e => e.ManagerID == managerId)
                                 .Include(e => e.Department)
                                 .Include(e => e.Role)
                                 .ToListAsync();
        }
        // ... implementation of other repository methods
    }

    // Generic Repository (for common CRUD operations)
    public interface IGenericRepository<T> where T : class
    {
        Task<T> GetByIdAsync(int id);
        Task<IEnumerable<T>> GetAllAsync();
        Task AddAsync(T entity);
        void Update(T entity);
        void Delete(T entity);
        Task SaveChangesAsync();
    }

    public class GenericRepository<T> : IGenericRepository<T> where T : class
    {
        protected readonly ApplicationDbContext _context;

        public GenericRepository(ApplicationDbContext context)
        {
            _context = context;
        }

        public async Task<T> GetByIdAsync(int id)
        {
            return await _context.Set<T>().FindAsync(id);
        }

        public async Task<IEnumerable<T>> GetAllAsync()
        {
            return await _context.Set<T>().ToListAsync();
        }

        public async Task AddAsync(T entity)
        {
            await _context.Set<T>().AddAsync(entity);
        }

        public void Update(T entity)
        {
            _context.Set<T>().Update(entity);
        }

        public void Delete(T entity)
        {
            _context.Set<T>().Remove(entity);
        }

        public async Task SaveChangesAsync()
        {
            await _context.SaveChangesAsync();
        }
    }
}





6.3 Business Logic Layer (BLL) Example:

TimeTrackingService

// Services/TimeTrackingService.cs
using EmployeeManagement.Domain.Models;
using EmployeeManagement.Infrastructure.Repositories;
using EmployeeManagement.Application.DTOs;
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Collections.Generic;

namespace EmployeeManagement.Application.Services
{
    public class TimeTrackingService
    {
        private readonly ITimeClockEntryRepository _timeClockEntryRepository;
        private readonly IEmployeeRepository _employeeRepository;

        public TimeTrackingService(ITimeClockEntryRepository timeClockEntryRepository, IEmployeeRepository employeeRepository)
        {
            _timeClockEntryRepository = timeClockEntryRepository;
            _employeeRepository = employeeRepository;
        }

        public async Task<bool> ClockInAsync(string userId)
        {
            var employee = await _employeeRepository.GetEmployeeByUserIdAsync(userId);
            if (employee == null) return false;

            // Check if already clocked in
            var activeEntry = await _timeClockEntryRepository.GetActiveEntryByEmployeeIdAsync(employee.EmployeeID);
            if (activeEntry != null)
            {
                // Already clocked in. Consider throwing an exception or returning specific error.
                return false;
            }

            var newEntry = new TimeClockEntry
            {
                EmployeeID = employee.EmployeeID,
                ClockInTime = DateTime.UtcNow,
                Status = TimeEntryStatus.Pending // Initially pending, can be auto-approved
            };
            await _timeClockEntryRepository.AddAsync(newEntry);
            await _timeClockEntryRepository.SaveChangesAsync();
            return true;
        }

        public async Task<bool> ClockOutAsync(string userId)
        {
            var employee = await _employeeRepository.GetEmployeeByUserIdAsync(userId);
            if (employee == null) return false;

            var activeEntry = await _timeClockEntryRepository.GetActiveEntryByEmployeeIdAsync(employee.EmployeeID);
            if (activeEntry == null)
            {
                // Not clocked in
                return false;
            }

            activeEntry.ClockOutTime = DateTime.UtcNow;
            activeEntry.DurationMinutes = (int)(activeEntry.ClockOutTime.Value - activeEntry.ClockInTime).TotalMinutes;
            activeEntry.Status = TimeEntryStatus.Approved; // Auto-approve on clock-out for simplicity

            _timeClockEntryRepository.Update(activeEntry);
            await _timeClockEntryRepository.SaveChangesAsync();
            return true;
        }

        public async Task<IEnumerable<TimeClockEntryDto>> GetEmployeeTimeLogsAsync(string userId, DateTime? startDate, DateTime? endDate)
        {
            var employee = await _employeeRepository.GetEmployeeByUserIdAsync(userId);
            if (employee == null) return Enumerable.Empty<TimeClockEntryDto>();

            var entries = await _timeClockEntryRepository.GetEntriesByEmployeeIdAsync(employee.EmployeeID, startDate, endDate);

            return entries.Select(e => new TimeClockEntryDto
            {
                EntryID = e.EntryID,
                ClockInTime = e.ClockInTime,
                ClockOutTime = e.ClockOutTime,
                DurationMinutes = e.DurationMinutes,
                Status = e.Status.ToString()
            });
        }

        // DTOs for clean API responses
        public class TimeClockEntryDto
        {
            public long EntryID { get; set; }
            public DateTime ClockInTime { get; set; }
            public DateTime? ClockOutTime { get; set; }
            public int? DurationMinutes { get; set; }
            public string Status { get; set; }
        }
    }
}


6.4 API Layer Example:
TimeTrackingController (ASP.NET Core Web API)

// API/Controllers/TimeTrackingController.cs
using EmployeeManagement.Application.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;
using System.Threading.Tasks;
using System.Collections.Generic;

namespace EmployeeManagement.Api.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    [Authorize] // All actions require authentication
    public class TimeTrackingController : ControllerBase
    {
        private readonly TimeTrackingService _timeTrackingService;

        public TimeTrackingController(TimeTrackingService timeTrackingService)
        {
            _timeTrackingService = timeTrackingService;
        }

        /// <summary>
        /// Allows an employee to clock in.
        /// </summary>
        [HttpPost("clockin")]
        [Authorize(Roles = "Employee,Manager,HR Admin")] // Only these roles can clock in
        public async Task<IActionResult> ClockIn()
        {
            var userId = User.FindFirstValue(ClaimTypes.NameIdentifier); // Get current user's ID
            if (string.IsNullOrEmpty(userId)) return Unauthorized();

            var result = await _timeTrackingService.ClockInAsync(userId);
            if (!result) return BadRequest("Already clocked in or unable to clock in.");

            return Ok("Clocked in successfully.");
        }

        /// <summary>
        /// Allows an employee to clock out.
        /// </summary>
        [HttpPost("clockout")]
        [Authorize(Roles = "Employee,Manager,HR Admin")]
        public async Task<IActionResult> ClockOut()
        {
            var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
            if (string.IsNullOrEmpty(userId)) return Unauthorized();

            var result = await _timeTrackingService.ClockOutAsync(userId);
            if (!result) return BadRequest("Not currently clocked in or unable to clock out.");

            return Ok("Clocked out successfully.");
        }

        /// <summary>
        /// Gets time logs for the authenticated employee.
        /// </summary>
        [HttpGet("my-logs")]
        [Authorize(Roles = "Employee,Manager,HR Admin")]
        [ProducesResponseType(typeof(IEnumerable<TimeTrackingService.TimeClockEntryDto>), 200)]
        public async Task<ActionResult<IEnumerable<TimeTrackingService.TimeClockEntryDto>>> GetMyTimeLogs(DateTime? startDate, DateTime? endDate)
        {
            var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
            if (string.IsNullOrEmpty(userId)) return Unauthorized();

            var logs = await _timeTrackingService.GetEmployeeTimeLogsAsync(userId, startDate, endDate);
            return Ok(logs);
        }

        // Add more endpoints for managers/HR to view team/all time logs
        // e.g., [HttpGet("team-logs")] [Authorize(Roles = "Manager,HR Admin")]
        // e.g., [HttpGet("all-logs")] [Authorize(Roles = "HR Admin")]
    }
}


6.5 Presentation Layer Example:
Blazor Component (Conceptual Time Tracking)

<!-- Pages/TimeTracking.razor (Blazor Component) -->
@page "/timetracking"
@using EmployeeManagement.Application.Services
@inject HttpClient Http
@inject NavigationManager NavManager

<h3>My Time Tracking</h3>

@if (errorMessage != null)
{
    <div class="alert alert-danger">@errorMessage</div>
}

<div class="time-controls">
    <button class="btn btn-primary" @onclick="ClockIn" disabled="@isClockedIn">Clock In</button>
    <button class="btn btn-danger" @onclick="ClockOut" disabled="@(!isClockedIn)">Clock Out</button>
    <p class="status-message">Current Status: <strong>@(isClockedIn ? "Clocked In" : "Clocked Out")</strong></p>
</div>

<h4>My Recent Time Logs</h4>
@if (timeLogs == null)
{
    <p><em>Loading time logs...</em></p>
}
else if (!timeLogs.Any())
{
    <p>No time logs found.</p>
}
else
{
    <table class="table table-striped">
        <thead>
            <tr>
                <th>Clock In</th>
                <th>Clock Out</th>
                <th>Duration (Minutes)</th>
                <th>Status</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var log in timeLogs)
            {
                <tr>
                    <td>@log.ClockInTime.ToLocalTime().ToString("g")</td>
                    <td>@(log.ClockOutTime?.ToLocalTime().ToString("g") ?? "N/A")</td>
                    <td>@(log.DurationMinutes ?? 0)</td>
                    <td>@log.Status</td>
                </tr>
            }
        </tbody>
    </table>
}

@code {
    private bool isClockedIn = false; // This state would ideally come from API
    private IEnumerable<TimeTrackingService.TimeClockEntryDto> timeLogs;
    private string errorMessage;

    protected override async Task OnInitializedAsync()
    {
        await LoadTimeLogs();
        // Determine initial clock-in status (e.g., fetch last entry from API)
        isClockedIn = timeLogs.Any() && timeLogs.OrderByDescending(l => l.ClockInTime).FirstOrDefault()?.ClockOutTime == null;
    }

    private async Task ClockIn()
    {
        try
        {
            var response = await Http.PostAsync("api/TimeTracking/clockin", null);
            response.EnsureSuccessStatusCode(); // Throws on 4xx/5xx
            isClockedIn = true;
            errorMessage = null;
            await LoadTimeLogs();
        }
        catch (HttpRequestException ex)
        {
            errorMessage = $"Error clocking in: {ex.Message}";
        }
    }

    private async Task ClockOut()
    {
        try
        {
            var response = await Http.PostAsync("api/TimeTracking/clockout", null);
            response.EnsureSuccessStatusCode();
            isClockedIn = false;
            errorMessage = null;
            await LoadTimeLogs();
        }
        catch (HttpRequestException ex)
        {
            errorMessage = $"Error clocking out: {ex.Message}";
        }
    }

    private async Task LoadTimeLogs()
    {
        try
        {
            // Fetch logs for the past 30 days, for example
            var endDate = DateTime.Now;
            var startDate = endDate.AddDays(-30);
            timeLogs = await Http.GetFromJsonAsync<IEnumerable<TimeTrackingService.TimeClockEntryDto>>($"api/TimeTracking/my-logs?startDate={startDate:yyyy-MM-dd}&endDate={endDate:yyyy-MM-dd}");
            errorMessage = null;
        }
        catch (HttpRequestException ex)
        {
            errorMessage = $"Error loading time logs: {ex.Message}";
        }
    }
}


Lets refine using this frame !! thanks...