Skip to content

Engineer Quickstart

This quickstart helps engineers get oriented with ConnectSoft-generated code, templates, and libraries. It is written for developers and engineers who will work with Factory-generated services and ConnectSoft patterns.

As an engineer at ConnectSoft, you'll work with generated code, extend it with domain logic, integrate with platforms, and ensure code quality through testing and observability.

Your Goals as an Engineer Here

  • Work with Generated Code - Understand and extend Factory-generated services
  • Add Domain Logic - Implement business logic in the correct layers
  • Integrate with Platforms - Connect services to Identity, Audit, Config, Bot platforms
  • Write Tests - Ensure code quality through comprehensive testing
  • Monitor and Debug - Use observability tools to monitor and debug services

Top 5 Docs to Read First

  1. Getting Started with Factory - How to use the Factory
  2. Microservice Template - Structure of generated services
  3. Libraries Catalog - Available libraries and how to use them
  4. Testing Strategy - Testing approach and best practices
  5. Observability-Driven Design - How to monitor and debug services

Working with Generated Code

Understanding Generated Structure

Generated services follow Clean Architecture:

src/
  ServiceName.Api/          # API layer (controllers, endpoints)
  ServiceName.Application/  # Application layer (use cases, handlers)
  ServiceName.Domain/       # Domain layer (entities, aggregates, business rules)
  ServiceName.Infrastructure/ # Infrastructure layer (repositories, external services)
tests/
  ServiceName.Tests/        # Unit and integration tests

See: Microservice Template for detailed structure.

Where to Add Your Code

  • Domain Layer - Add business logic, domain rules, entities
  • Application Layer - Add use cases, command/query handlers
  • Infrastructure Layer - Add external integrations, repositories
  • API Layer - Add endpoints, request/response models

Important

Never add business logic to the API or Infrastructure layers. Business logic belongs in the Domain layer.

Adding New Features

Step 1: Define Domain Logic

Add entities, aggregates, and business rules to the Domain layer:

// Domain/Order.cs
public class Order : AggregateRoot
{
    public OrderId Id { get; private set; }
    public TenantId TenantId { get; private set; }
    public OrderStatus Status { get; private set; }

    public void Complete()
    {
        if (Status != OrderStatus.Pending)
            throw new InvalidOperationException("Only pending orders can be completed");

        Status = OrderStatus.Completed;
        AddDomainEvent(new OrderCompleted(Id));
    }
}

Step 2: Add Application Logic

Add use cases and handlers to the Application layer:

// Application/Commands/CompleteOrderCommand.cs
public class CompleteOrderCommand : IRequest
{
    public OrderId OrderId { get; set; }
}

public class CompleteOrderHandler : IRequestHandler<CompleteOrderCommand>
{
    private readonly IOrderRepository _repository;

    public async Task Handle(CompleteOrderCommand request, CancellationToken cancellationToken)
    {
        var order = await _repository.GetByIdAsync(request.OrderId);
        order.Complete();
        await _repository.SaveAsync(order);
    }
}

Step 3: Add API Endpoint

Add controller endpoint to the API layer:

// Api/Controllers/OrdersController.cs
[ApiController]
[Route("api/orders")]
public class OrdersController : ControllerBase
{
    private readonly IMediator _mediator;

    [HttpPost("{orderId}/complete")]
    public async Task<IActionResult> CompleteOrder(OrderId orderId)
    {
        await _mediator.Send(new CompleteOrderCommand { OrderId = orderId });
        return Ok();
    }
}

Step 4: Add Tests

Add tests for your feature:

// Tests/Domain/OrderTests.cs
[Fact]
public void Complete_WhenPending_ShouldSetStatusToCompleted()
{
    var order = new Order(OrderId.New(), TenantId.New());
    order.Complete();

    Assert.Equal(OrderStatus.Completed, order.Status);
}

See: Testing Strategy for testing guidance.

Debugging and Observability

Using Logs

Generated services include structured logging:

_logger.LogInformation("Order {OrderId} completed for tenant {TenantId}", orderId, tenantId);

Using Traces

Distributed tracing is built-in:

using var activity = _activitySource.StartActivity("ProcessOrder");
activity?.SetTag("order.id", orderId);
// ... processing

Using Metrics

Metrics are automatically collected:

  • Request counts
  • Response times
  • Error rates
  • Business metrics

See: Observability-Driven Design for detailed guidance.

Common Tasks

Task: Add External Integration

  1. Add Client to Infrastructure

    // Infrastructure/External/PaymentProviderClient.cs
    public class PaymentProviderClient : IPaymentProvider
    {
        private readonly HttpClient _httpClient;
        // ... implementation
    }
    

  2. Register in DI

    services.AddScoped<IPaymentProvider, PaymentProviderClient>();
    

  3. Use in Application Layer

    public class ProcessPaymentHandler
    {
        private readonly IPaymentProvider _paymentProvider;
        // ... use payment provider
    }
    

Task: Add Domain Event Handler

  1. Create Handler

    // Application/EventHandlers/OrderCompletedHandler.cs
    public class OrderCompletedHandler : INotificationHandler<OrderCompleted>
    {
        public async Task Handle(OrderCompleted notification, CancellationToken cancellationToken)
        {
            // Handle event
        }
    }
    

  2. Register Handler

    services.AddScoped<INotificationHandler<OrderCompleted>, OrderCompletedHandler>();
    

See: Event-Driven Mindset for event patterns.