"pH7x Systems Project XYZ's API"

"Beyond technology, Project XYZ is committed to integrity, respect, and positive impact in every project."

"pH7x Systems Project XYZ's API"

Tuesday, June 17, 2025

Project XYZ – Solution & Folder Structure

This solution is organized for best-practice, scalable ASP.NET Core API development. It contains two main projects and a modular folder structure for maintainability and growth.

Projects

  • Project XYZ.API The main ASP.NET Core Web API project. Contains business logic, endpoints, models, services, repositories, configuration, and more.

  • Project XYZ.Tests The xUnit test project for unit/integration tests. References the API project for direct testing of logic and controllers.

Main Project Folders (Project XYZ.API/)

Folder Description
Controllers/ API endpoints (e.g., UserController.cs).
Domain/ Core business entities/models (e.g., User.cs).
Dtos/ Data Transfer Objects for API in/out.
Services/ Business logic layer (e.g., UserService.cs).
Repositories/ Data access layer (e.g., UserRepository.cs).
Interfaces/ Abstractions for services, repos, tokens, etc.
Data/ EF Core DbContext and data seeding.
Mappers/ Logic for mapping between entities and DTOs.
Middlewares/ Cross-cutting concerns (e.g., error handling middleware).
Configuration/ Dependency injection and app configuration.
Extensions/ Extension methods for utility code.
Helpers/ Utility/helper classes.
Migrations/ EF Core database migrations.
Features/ (Optional) Feature-based folders for large, modular code.

Example Feature-Based Structure

Project XYZ.API/Features/User/
    Controllers/
    Services/
    Repositories/
    Dtos/
    Mappers/

Repeat for each major feature (e.g., Account, Speciality, etc.) as your API grows.

Key Sample Files

  • Controllers/UserController.cs Example endpoint: GET /User/{id}.
  • Domain/User.cs Example business entity.
  • Dtos/UserDto.cs DTO for user data exchange.
  • Services/UserService.cs Business logic for users.
  • Repositories/UserRepository.cs Data access for users.
  • Interfaces/IUserService.cs, IUserRepository.cs, ITokenService.cs Abstractions for clean architecture.
  • Mappers/UserMapper.cs Maps between domain and DTO.
  • Middlewares/ErrorHandlingMiddleware.cs Handles global errors.
  • Configuration/DependencyInjection.cs Central DI/service registration.

Test Project Structure (Project XYZ.Tests/)

  • Controllers/UserControllerTests.cs Unit tests for API endpoints.
  • Services/UserServiceTests.cs Unit tests for service logic.

Program Entry

  • Program.cs Minimal, modern .NET startup:

    • Registers all dependencies
    • Adds Swagger for API docs
    • Configures endpoints and middleware

SH File Templating

#!/bin/bash

# Replace YOUR_PROJECT

# Solution & Project Names
SOLUTION_NAME="YOUR_PROJECT"
PROJECT_NAME="YOUR_PROJECT.API"
TEST_PROJECT_NAME="YOUR_PROJECT.Tests"

# 1. Create Solution & Projects
dotnet new sln -n $SOLUTION_NAME
dotnet new webapi -n $PROJECT_NAME
dotnet new xunit -n $TEST_PROJECT_NAME
dotnet sln add $PROJECT_NAME/$PROJECT_NAME.csproj
dotnet sln add $TEST_PROJECT_NAME/$TEST_PROJECT_NAME.csproj
dotnet add $TEST_PROJECT_NAME/$TEST_PROJECT_NAME.csproj reference $PROJECT_NAME/$PROJECT_NAME.csproj

# 2. Create core folders
FOLDERS=("Controllers" "Domain" "Dtos" "Services" "Repositories" "Interfaces" "Data" "Mappers" "Middlewares" "Configuration" "Extensions" "Helpers" "Migrations")
for folder in "${FOLDERS[@]}"; do
    mkdir -p $PROJECT_NAME/$folder
    touch $PROJECT_NAME/$folder/.gitkeep
done

# 3. Feature-based folders (example)
FEATURES=("User" "Account" "Speciality")
for feature in "${FEATURES[@]}"; do
    mkdir -p $PROJECT_NAME/Features/$feature/Controllers
    mkdir -p $PROJECT_NAME/Features/$feature/Services
    mkdir -p $PROJECT_NAME/Features/$feature/Repositories
    mkdir -p $PROJECT_NAME/Features/$feature/Dtos
    mkdir -p $PROJECT_NAME/Features/$feature/Mappers
    touch $PROJECT_NAME/Features/$feature/.gitkeep
done

# 4. Sample classes

# Interface: Repository
cat > $PROJECT_NAME/Interfaces/IUserRepository.cs < GetByIdAsync(int id);
        Task> GetAllAsync();
        Task AddAsync(User user);
        Task UpdateAsync(User user);
        Task DeleteAsync(int id);
    }
}
EOL

# Interface: Service
cat > $PROJECT_NAME/Interfaces/IUserService.cs < GetUserAsync(int id);
        Task> GetAllUsersAsync();
        Task CreateUserAsync(UserDto userDto);
        Task UpdateUserAsync(int id, UserDto userDto);
        Task DeleteUserAsync(int id);
    }
}
EOL

# Interface: TokenService
cat > $PROJECT_NAME/Interfaces/ITokenService.cs < $PROJECT_NAME/Services/UserService.cs < GetUserAsync(int id) => throw new NotImplementedException();
        public Task> GetAllUsersAsync() => throw new NotImplementedException();
        public Task CreateUserAsync(UserDto userDto) => throw new NotImplementedException();
        public Task UpdateUserAsync(int id, UserDto userDto) => throw new NotImplementedException();
        public Task DeleteUserAsync(int id) => throw new NotImplementedException();
    }
}
EOL

# Repository
cat > $PROJECT_NAME/Repositories/UserRepository.cs < GetByIdAsync(int id) => throw new NotImplementedException();
        public Task> GetAllAsync() => throw new NotImplementedException();
        public Task AddAsync(User user) => throw new NotImplementedException();
        public Task UpdateAsync(User user) => throw new NotImplementedException();
        public Task DeleteAsync(int id) => throw new NotImplementedException();
    }
}
EOL

# Controller - WITH GetUser endpoint!
cat > $PROJECT_NAME/Controllers/UserController.cs < GetUser(int id)
        {
            var user = await _userService.GetUserAsync(id);
            if (user == null)
                return NotFound();
            return Ok(user);
        }

        // Other endpoints using _userService can go here
    }
}
EOL

# Middleware Interface (not mandatory, but pattern example)
cat > $PROJECT_NAME/Interfaces/IMiddleware.cs < $PROJECT_NAME/Middlewares/ErrorHandlingMiddleware.cs < $PROJECT_NAME/Configuration/DependencyInjection.cs <();
            services.AddScoped();
            // Add more as needed
            return services;
        }
    }
}
EOL

# Domain Entity
cat > $PROJECT_NAME/Domain/User.cs < $PROJECT_NAME/Dtos/UserDto.cs < $PROJECT_NAME/Mappers/UserMapper.cs < new UserDto { Id = user.Id, Name = user.Name };
        public static User ToDomain(UserDto dto) => new User { Id = dto.Id, Name = dto.Name };
    }
}
EOL

# Tests

# Add Moq to test project
dotnet add $TEST_PROJECT_NAME package Moq

# Create test folders and files
mkdir -p $TEST_PROJECT_NAME/Services $TEST_PROJECT_NAME/Controllers

# UserService unit tests
cat > $TEST_PROJECT_NAME/Services/UserServiceTests.cs < _userRepoMock;

        public UserServiceTests()
        {
            _userRepoMock = new Mock();
            _userService = new UserService(_userRepoMock.Object);
        }

        [Fact]
        public async Task GetUserAsync_ReturnsUserDto_WhenUserExists()
        {
            var user = new User { Id = 1, Name = "Test User" };
            _userRepoMock.Setup(r => r.GetByIdAsync(1)).ReturnsAsync(user);

            var result = await _userService.GetUserAsync(1);

            Assert.NotNull(result);
            Assert.Equal(user.Id, result.Id);
            Assert.Equal(user.Name, result.Name);
        }

        [Fact]
        public async Task GetUserAsync_ReturnsNull_WhenUserDoesNotExist()
        {
            _userRepoMock.Setup(r => r.GetByIdAsync(2)).ReturnsAsync((User?)null);

            var result = await _userService.GetUserAsync(2);

            Assert.Null(result);
        }
    }
}
EOL

cat > $TEST_PROJECT_NAME/Controllers/UserControllerTests.cs < _userServiceMock;

        public UserControllerTests()
        {
            _userServiceMock = new Mock();
            _controller = new UserController(_userServiceMock.Object);
        }

        [Fact]
        public async Task GetUser_ReturnsOk_WhenUserExists()
        {
            var userDto = new UserDto { Id = 1, Name = "Test User" };
            _userServiceMock.Setup(s => s.GetUserAsync(1)).ReturnsAsync(userDto);

            var result = await _controller.GetUser(1);

            var okResult = Assert.IsType(result);
            var returnedUser = Assert.IsType(okResult.Value);
            Assert.Equal(userDto.Id, returnedUser.Id);
        }

        [Fact]
        public async Task GetUser_ReturnsNotFound_WhenUserDoesNotExist()
        {
            _userServiceMock.Setup(s => s.GetUserAsync(99)).ReturnsAsync((UserDto?)null);

            var result = await _controller.GetUser(99);

            Assert.IsType(result);
        }
    }
}
EOL

cat > $PROJECT_NAME/Program.cs <
{
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "${PROJECT_NAME}", Version = "v1" });
});

var app = builder.Build();

// Use Swagger in dev
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

// Optionally: Use global error handling middleware
// app.UseMiddleware<${PROJECT_NAME}.Middlewares.ErrorHandlingMiddleware>();

app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();

app.Run();
EOL


# Restore, clean, and build solution
dotnet restore
dotnet clean
dotnet build

echo "API Solution, folders, interfaces, and sample classes created!"
echo "Open in VS Code with: code ."

This structure enables clean, maintainable, and testable APIs for any professional project.

Entre em contacto connosco para qualquer questão ou pedido de serviço.
Se pretende candidatar-se a uma oportunidade, por favor inicie sessão ou registe-se primeiro.

Sobre a pH7x Systems

A pH7x Systems é um fornecedor especializado em tecnologia, oferecendo soluções robustas em Infraestrutura Cloud, Microsoft 365 e desenvolvimento em SharePoint.

Capacitamos empresas com serviços personalizados em gestão de bases de dados, projetos de migração e operações de cibersegurança. Com compromisso na transparência e resultados, a nossa equipa é movida pela excelência em consultoria e entrega nearshore.

Vamos construir a próxima solução juntos — o seu sucesso é a nossa missão.