"pH7x Systems Project XYZ's API"

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

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.

Protected by reCAPTCHA · Privacy · Terms

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.