Skip to content

Coding Standards

This document provides coding standards and guidelines for developers and AI code generators working on ConnectSoft projects. Rather than duplicating comprehensive rule sets, this document references established analyzer repositories and highlights key rules that are particularly important for maintaining code quality and consistency.

Primary References

The following analyzer repositories provide comprehensive rule sets and should be referenced for detailed guidelines:

  • StyleCopAnalyzers: Implementation of StyleCop rules using the .NET Compiler Platform (Roslyn). Provides code style and consistency rules for C# code. See the documentation for a complete list of rules.

  • AspNetCoreAnalyzers: Analyzers for ASP.NET Core applications that identify best practices and potential issues specific to web development.

  • DocumentationAnalyzers: Analyzers that improve the quality of XML documentation comments in C# code, ensuring proper documentation standards.

Naming Conventions

"There are only two hard things in Computer Science: cache invalidation and naming things."
Phil Karlton

Consistent naming conventions help maintain clarity, readability, and maintainability of the codebase. The following guidelines outline the standards used in ConnectSoft projects. These conventions follow Microsoft .NET Framework Design Guidelines while incorporating domain-driven design and Clean Architecture principles.

Test Methods

  • Test names should NOT contain underscores: Use descriptive names with proper casing instead of underscores.
  • ✅ Good: ShouldReturnUserWhenIdIsValid(), CanProcessOrderWithValidItems()
  • ❌ Bad: Should_Return_User_When_Id_Is_Valid(), Can_Process_Order_With_Valid_Items()

General Naming Conventions

Case Styles

Case Style Usage Example
PascalCase Classes, methods, properties, namespaces, constants, enums UserService, GetUserInfo, OrderDetails, MaxRetries
camelCase Variables, parameters, private fields userName, totalAmount, logger
UPPER_CASE Rarely used (only for very specific constants or legacy code) Not recommended

Abbreviations

  • Avoid abbreviations unless widely accepted
  • Accepted abbreviations: Id, Http, Https, Xml, Json, Api, Uri, Url, Db, Di, UI, IO
  • Avoid: Usr, Ord, Cust, Mgr (use full words: User, Order, Customer, Manager)

One Class Per File

Each class, interface, enum, or struct should be in its own file with a matching filename.

// ✅ GOOD - UserService.cs
public class UserService { }

// ❌ BAD - Multiple classes in one file
public class UserService { }
public class OrderService { } // Should be in OrderService.cs

Projects and Namespaces

Project Naming

Projects follow the pattern: ConnectSoft.{ProjectName}.{Layer}[.{Technology}]

Pattern Example Description
ConnectSoft.{ProjectName} Base project Common code, constants, exceptions
ConnectSoft.{ProjectName}.{Layer} EntityModel, DomainModel, ServiceModel Layer-specific projects
ConnectSoft.{ProjectName}.{Layer}.{Technology} PersistenceModel.NHibernate, ServiceModel.RestApi Technology-specific implementations
ConnectSoft.{ProjectName}.{Layer}.Impl DomainModel.Impl Implementation projects (separated from contracts)

Namespace Naming

Namespaces match project structure exactly:

// Project: ConnectSoft.MicroserviceTemplate.EntityModel
namespace ConnectSoft.MicroserviceTemplate.EntityModel
{
    public class MicroserviceAggregateRoot { }
}

Classes and Interfaces

Class Naming

  • Use PascalCase
  • Use descriptive nouns
  • Avoid generic names like Manager, Handler, Helper (be specific)
Pattern Example Description
Entity UserEntity, OrderEntity Domain entities
Repository UserRepository, OrderRepository Data access repositories
Service UserService, OrderProcessor Business logic services
Controller UsersController, OrdersServiceController API controllers
Options MicroserviceOptions, MongoDbOptions Configuration options
Extensions ServiceCollectionExtensions, MongoDbExtensions Extension method containers
Constants MicroserviceConstants, ServiceModelConstants Constant containers

Interface Naming

  • Prefix with I
  • Use PascalCase
  • Match implementation naming (without I prefix)
Interface Implementation Pattern
IUserRepository UserRepository Standard interface
IMicroserviceAggregateRoot MicroserviceAggregateRootEntity Entity interface
IMicroserviceAggregateRootsProcessor DefaultMicroserviceAggregateRootsProcessor Service interface
IUnitOfWork NHibernateUnitOfWork, MongoDbUnitOfWork Abstract interface

Read-Only Interface Pattern:

// Read-only interface
public interface IUserReadOnly { }

// Implementation
public class UserReadOnlyEntity : IUserReadOnly { }

Methods and Properties

Method Naming

  • Use PascalCase
  • Use verbs to describe actions
  • Be specific about what the method does
Pattern Example Description
Get GetUserById, GetAllUsers Retrieval operations
Create CreateUser, CreateOrder Creation operations
Update UpdateUser, UpdateOrderStatus Update operations
Delete DeleteUser, DeleteOrder Deletion operations
Process ProcessPayment, ProcessOrder Business processing
Validate ValidateUser, ValidateOrder Validation operations
Async Methods GetUserByIdAsync, CreateUserAsync Async methods with Async suffix

Avoid: - ❌ Generic names: Get, Set, Update, Do - ❌ Vague names: Handle, Process, Execute (without context)

Property Naming

  • Use PascalCase
  • Use nouns or noun phrases
  • Boolean properties should be questions or statements: IsActive, HasItems, CanDelete
Pattern Example Description
Simple UserName, Email, OrderDate Simple properties
Boolean IsActive, HasPermission, CanEdit Boolean properties
Collection Users, OrderItems, Permissions Collection properties
ID UserId, OrderId, ObjectId Identifier properties (use Id, not ID)

Variables and Parameters

Variable Naming

  • Use camelCase
  • Use descriptive names
  • Avoid single letters (except in loops: i, j, k)
Pattern Example Description
Simple userName, orderId, totalAmount Simple variables
Collection users, orders, items Collection variables
Boolean isActive, hasItems, canDelete Boolean variables
Async Result result, response, entity Async method results

Parameter Naming

Parameters follow the same rules as variables:

public async Task<User> GetUserById(Guid userId, CancellationToken cancellationToken = default)
{
    // userId - camelCase parameter
    // cancellationToken - camelCase parameter with default value
}

Private Fields

Field Naming

  • Use camelCase with this. prefix (preferred in ConnectSoft templates)
  • Or use _ prefix (alternative style)
// ✅ GOOD - camelCase with this. prefix (template style)
public class UserService
{
    private readonly ILogger logger;
    private readonly IUserRepository repository;

    public UserService(ILogger logger, IUserRepository repository)
    {
        this.logger = logger;
        this.repository = repository;
    }
}

// ✅ ALTERNATIVE - _ prefix (also acceptable)
public class UserService
{
    private readonly ILogger _logger;
    private readonly IUserRepository _repository;
}

Constants

Constant Naming

  • Use PascalCase (not UPPER_CASE)
  • Group related constants in a static class
  • Use descriptive names
// ✅ GOOD - PascalCase constants
public static class MicroserviceConstants
{
    public const string NHibernateDIKey = "NHibernateDIKey";
    public const string MongoDbDIKey = "MongoDbDIKey";
}

// ❌ BAD - UPPER_CASE (not used in ConnectSoft templates)
public const string MAX_CONNECTIONS = "100";

Constant Patterns

Pattern Example Usage
Section Names MicroserviceOptionsSectionName Configuration section names
DI Keys NHibernateDIKey, MongoDbDIKey Dependency injection keys
Namespaces BaseNamespace, MessagesNamespace Service model namespaces
Meter Names MicroserviceTemplateMetricsMeterName Metrics meter names

Enumerations

Enum Naming

  • Use PascalCase
  • Use singular names for the enum type
  • Use PascalCase for enum values
public enum OrderStatus
{
    Pending,
    Processing,
    Completed,
    Cancelled
}

Exceptions

Exception Naming

  • Suffix with Exception
  • Use descriptive names
  • Indicate the problem
Pattern Example Description
Not Found UserNotFoundException, OrderNotFoundException Entity not found
Invalid InvalidOperationException, InvalidConfigurationException Invalid state/configuration
Validation ValidationException, OptionsValidationException Validation failures
public class UserNotFoundException : Exception
{
    public UserNotFoundException(Guid userId) 
        : base($"User with ID {userId} was not found.") { }
}

Service Model Naming

Request/Response DTOs

Pattern Example Description
Request CreateUserRequest, GetUserDetailsRequest Request DTOs
Response CreateUserResponse, GetUserDetailsResponse Response DTOs
DTO UserDto, OrderDto Data transfer objects
// Service Model Request/Response
public class CreateMicroserviceAggregateRootRequest { }
public class CreateMicroserviceAggregateRootResponse { }
public class MicroserviceAggregateRootDto { }

Controller Naming

Controllers follow RESTful conventions:

Pattern Example Route
Resource Controller MicroserviceAggregateRootsServiceController api/MicroserviceAggregateRoots
Feature Controller FeatureAController api/FeatureA
[ApiController]
[Route("api/[controller]")]
public class MicroserviceAggregateRootsServiceController : ControllerBase
{
    [HttpPost("MicroserviceAggregateRoots/")]
    public async Task<CreateMicroserviceAggregateRootResponse> CreateMicroserviceAggregateRoot(...) { }
}

API Endpoint Naming

  • Use nouns (resources), not verbs
  • Use plural for collections
  • Use RESTful conventions
HTTP Method Pattern Example
GET GET /api/{resource} GET /api/MicroserviceAggregateRoots
POST POST /api/{resource} POST /api/MicroserviceAggregateRoots
PUT PUT /api/{resource}/{id} PUT /api/MicroserviceAggregateRoots/{id}
DELETE DELETE /api/{resource}/{id} DELETE /api/MicroserviceAggregateRoots/{id}

Avoid: - ❌ Verbs in URLs: /api/GetUser, /api/CreateOrder - ❌ Singular resource names: /api/User (use /api/Users)

Domain Model Naming

Input/Output Models

Pattern Example Description
Input CreateUserInput, GetUserDetailsInput Domain input models
Output CreateUserOutput, GetUserDetailsOutput Domain output models
// Domain Model Input/Output
public class CreateMicroserviceAggregateRootInput { }
public class GetMicroserviceAggregateRootDetailsInput { }
public class CreateMicroserviceAggregateRootOutput { }

Processors and Retrievers

Pattern Example Description
Processor Interface IMicroserviceAggregateRootsProcessor Write operations interface
Processor Implementation DefaultMicroserviceAggregateRootsProcessor Write operations implementation
Retriever Interface IMicroserviceAggregateRootsRetriever Read operations interface
Retriever Implementation DefaultMicroserviceAggregateRootsRetriever Read operations implementation

Entity Model Naming

Entity Contracts and Implementations

Pattern Example Description
Entity Interface IMicroserviceAggregateRoot Entity contract
Entity Implementation MicroserviceAggregateRootEntity Entity implementation
Read-Only Interface IUserReadOnly Read-only entity contract
Read-Only Implementation UserReadOnlyEntity Read-only entity implementation
public interface IMicroserviceAggregateRoot : IGenericEntity<Guid>
{
    Guid ObjectId { get; }
    string? SomeValue { get; }
}

public class MicroserviceAggregateRootEntity : MicroserviceAggregateRoot, IMicroserviceAggregateRoot
{
    public Guid ObjectId { get; set; }
    public string? SomeValue { get; set; }
}

Messaging Model Naming

Commands and Events

Pattern Example Description
Command CreateOrderCommand, ProcessPaymentCommand Command messages
Event OrderCreatedEvent, PaymentProcessedEvent Event messages (past tense)
// Commands - Imperative verbs
public class CreateMicroserviceAggregateRootCommand : ICommand
{
    public Guid ObjectId { get; set; }
}

// Events - Past tense
public class MicroserviceAggregateRootCreatedEvent : IEvent
{
    public Guid ObjectId { get; set; }
    public DateTimeOffset CreatedAt { get; set; }
}

Best Practices: - ✅ Commands: Use imperative verbs (Create, Process, Approve) - ✅ Events: Use past tense (Created, Processed, Approved) - ❌ Avoid generic names: Message, Data, Info

Persistence Model Naming

Repositories

Pattern Example Description
Repository Interface IMicroserviceAggregateRootsRepository Repository contract
NHibernate Implementation MicroserviceAggregateRootsRepository NHibernate repository
MongoDB Implementation MicroserviceAggregateRootsMongoDbRepository MongoDB repository

Specifications

Pattern Example Description
Specification Interface IMicroserviceAggregateRootsSpecification Specification contract
NHibernate Specification MicroserviceAggregateRootsQueryableSpecification NHibernate queryable specification
MongoDB Specification MicroserviceAggregateRootsMongoDbQueryableSpecification MongoDB queryable specification

Actor Model Naming

Actor Interfaces and Implementations

Pattern Example Description
Actor Interface IBankAccountActor Actor contract
Actor Implementation BankAccountActor Actor implementation
Actor Input WithdrawInput, DepositInput Actor input models
Actor Output WithdrawOutput, DepositOutput Actor output models
public interface IBankAccountActor : IGrainWithGuidKey
{
    Task<WithdrawOutput> Withdraw(WithdrawInput input);
}

public class BankAccountActor : Grain, IBankAccountActor
{
    public Task<WithdrawOutput> Withdraw(WithdrawInput input) { }
}

Database Naming

Tables and Collections

Database Pattern Example Description
SQL Tables PascalCase, plural Users, Orders, MicroserviceAggregateRoots SQL table names
MongoDB Collections PascalCase, plural Users, Orders, MicroserviceAggregateRoots MongoDB collection names
Schemas Namespace-style ConnectSoft.MicroserviceTemplate SQL schema names

Columns and Fields

Pattern Example Description
SQL Columns PascalCase ObjectId, SomeValue, CreatedOn
MongoDB Fields PascalCase ObjectId, SomeValue, CreatedOn

Indexes

Pattern Example Description
SQL Indexes IX_{Table}_{Column} IX_Users_Email, IX_Orders_CustomerId
Composite Indexes IX_{Table}_{Column1}_{Column2} IX_Orders_Status_CreatedOn

Configuration and Options

Options Classes

Pattern Example Description
Options Class MicroserviceOptions, MongoDbOptions Configuration options
Section Name Constant MicroserviceOptionsSectionName Configuration section name
Validator ValidateMicroserviceOptions Options validator
public sealed class MicroserviceOptions
{
    public const string MicroserviceOptionsSectionName = "Microservice";

    [Required]
    required public string MicroserviceName { get; set; }
}

Extension Methods

Extension Method Naming

Extension method classes follow the pattern: {TargetType}Extensions

Pattern Example Description
Service Collection ServiceCollectionExtensions, MongoDbExtensions DI registration extensions
Application Builder ApplicationBuilderExtensions Middleware pipeline extensions
Endpoint Route Builder EndpointRouteBuilderExtensions Endpoint mapping extensions

Naming Pattern: Add{Feature}, Use{Feature}, Map{Feature}

File and Folder Naming

Files

  • Match class name: UserService.cs contains UserService class
  • Use PascalCase: OrderRepository.cs, MicroserviceOptions.cs
  • One class per file: Each file contains one primary class/interface

Folders

Folders follow namespace structure and use PascalCase:

Folder Purpose Example
Controllers API controllers Controllers/, RestApi/
Repositories Repository implementations Repositories/
Mappings Entity mappings Mappings/, NHibernate/, MongoDb/
Options Configuration options Root of Options project
Extensions Extension methods Root of ApplicationModel project

Metrics Naming

Metric Names

Metrics use lowercase with dots (OpenTelemetry convention):

Pattern Example Description
Meter Name connectsoft.microservicetemplate Meter name
Counter connectsoft.microservicetemplate.aggregateroot.added Counter metric
Duration connectsoft.microservicetemplate.aggregateroot.add.duration Duration metric

Use Cases and Operations Naming

Best Practices

  • Use verbs to describe actions
  • Avoid generic names like Get, Set, Update

Examples

E-commerce Domain: - AddItemToCart - PlaceOrder - CancelOrder

User Management Domain: - RegisterUser - ChangeUserPassword - DeactivateUserAccount

Financial Domain: - InitiateTransfer - ProcessRefund - ReconcileAccount

Naming Conventions Best Practices

Do's

  1. Use PascalCase for classes, methods, properties, constants
  2. Use camelCase for variables and parameters
  3. Be descriptive - names should clearly indicate purpose
  4. Follow established patterns - use template conventions consistently
  5. Match file names to class names - one class per file
  6. Use verbs for methods - CreateUser, GetOrderDetails
  7. Use nouns for classes - UserService, OrderRepository
  8. Prefix interfaces with I - IUserRepository, IOrderService
  9. Suffix exceptions with Exception - UserNotFoundException
  10. Use Async suffix for async methods - GetUserAsync

Don'ts

  1. Don't use abbreviations unless widely accepted (Id, Http)
  2. Don't use generic names - Manager, Handler, Helper
  3. Don't use Hungarian notation - strUserName, intCount
  4. Don't use UPPER_CASE for constants (use PascalCase)
  5. Don't mix naming styles - be consistent within a project
  6. Don't use verbs in URLs - /api/GetUser ❌ → /api/Users
  7. Don't use singular for resource collections - /api/User ❌ → /api/Users
  8. Don't skip Async suffix for async methods
  9. Don't use single letters (except in loops)
  10. Don't use magic strings - use constants instead

For additional naming rules, refer to StyleCopAnalyzers naming rules.

Code Style

StyleCopAnalyzers enforces a wide range of code style rules. Key categories include:

  • Layout Rules: Spacing, indentation, and code organization
  • Readability Rules: Code clarity and maintainability
  • Maintainability Rules: Code structure and complexity
  • Ordering Rules: Member ordering within classes

For complete details, see the StyleCopAnalyzers documentation.

Documentation

XML documentation comments are required for public APIs and should follow standard conventions:

  • Use proper XML documentation tags (<summary>, <param>, <returns>, <exception>, etc.)
  • Provide meaningful descriptions for all public members
  • Include parameter descriptions
  • Document return values and exceptions

Refer to DocumentationAnalyzers for comprehensive documentation guidelines and rules.

ASP.NET Core Best Practices

When developing ASP.NET Core applications, follow best practices identified by AspNetCoreAnalyzers:

  • Proper use of dependency injection
  • Correct middleware ordering
  • Appropriate use of async/await patterns
  • Security best practices
  • Performance optimization guidelines

See AspNetCoreAnalyzers for detailed ASP.NET Core-specific rules and recommendations.

Additional Guidelines

Code Organization

  • Follow the project's solution structure and naming conventions
  • Group related functionality together
  • Maintain clear separation of concerns

Error Handling

  • Use appropriate exception types
  • Provide meaningful error messages
  • Log errors appropriately

Performance

  • Avoid premature optimization
  • Use async/await for I/O-bound operations
  • Consider memory allocation patterns

Integration with IDEs

These analyzers integrate seamlessly with Visual Studio and other IDEs, providing: - Real-time code analysis - Automatic code fixes where available - IntelliSense integration - Build-time warnings and errors

Ensure that the appropriate analyzer NuGet packages are installed in your projects to benefit from these rules during development.

Configuration

Analyzer rules can be configured using: - Rule set files (.ruleset) in Visual Studio - EditorConfig files (.editorconfig) for cross-IDE compatibility - stylecop.json files for StyleCop-specific settings

Refer to each analyzer's documentation for configuration options and customization.