Files
interview-demo-code/.cursor/rules/architecture.md
Nikita Kiselev 393bbb286b
Some checks failed
Telegram Mini App Shop Builder / Compute version metadata (push) Has been cancelled
Telegram Mini App Shop Builder / Run Frontend tests (push) Has been cancelled
Telegram Mini App Shop Builder / Run Backend tests (push) Has been cancelled
Telegram Mini App Shop Builder / Run PHP_CodeSniffer (push) Has been cancelled
Telegram Mini App Shop Builder / Build module. (push) Has been cancelled
Telegram Mini App Shop Builder / release (push) Has been cancelled
Squashed commit message
2026-03-11 23:00:59 +03:00

3.8 KiB
Raw Blame History

Architectural Rules

ECommerce Framework Architecture

MVC-L Pattern

The project uses a modified MVC-L pattern (Model-View-Controller-Language):

  • Model: Classes in src/Models/ data access and database operations
  • View: Vue components on the frontend, JSON responses on the backend
  • Controller: Handlers in src/Handlers/ and bastion/Handlers/
  • Language: Translator in framework/Translator/

Dependency Injection

All dependencies are injected via the Container:

// ✅ Correct
public function __construct(
    private Builder $builder,
    private TelegramCustomer $telegramCustomerModel
) {}

// ❌ Incorrect
public function __construct() {
    $this->builder = new Builder(...);
}

Service Providers

Services are registered via Service Providers:

class MyServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->app->singleton(MyService::class, function ($app) {
            return new MyService($app->get(Dependency::class));
        });
    }
}

Routes

Routes are defined in routes.php:

return [
    'actionName' => [HandlerClass::class, 'methodName'],
];

Handlers (Controllers)

Handlers process HTTP requests:

class MyHandler
{
    public function handle(Request $request): JsonResponse
    {
        // Validation
        // Business logic via Services
        // Return JsonResponse
    }
}

Models

Models work with data:

class MyModel
{
    public function __construct(
        private ConnectionInterface $database,
        private Builder $builder
    ) {}
    
    public function findById(int $id): ?array
    {
        return $this->builder->newQuery()
            ->from($this->tableName)
            ->where('id', '=', $id)
            ->firstOrNull();
    }
}

Services

Services contain business logic:

class MyService
{
    public function __construct(
        private MyModel $model
    ) {}
    
    public function doSomething(array $data): array
    {
        // Business logic
        return $this->model->create($data);
    }
}

Migrations

Migrations live in database/migrations/:

return new class extends Migration {
    public function up(): void
    {
        $this->database->statement('CREATE TABLE ...');
    }
};

Query Builder

Always use Query Builder instead of raw SQL:

// ✅ Correct
$query = $this->builder->newQuery()
    ->select(['id', 'name'])
    ->from('table_name')
    ->where('status', '=', 'active')
    ->get();

// ❌ Incorrect
$result = $this->database->query("SELECT * FROM table_name WHERE status = 'active'");

Frontend Architecture

Admin Panel (Vue 3)

  • Composition API
  • Pinia for state management
  • PrimeVue for UI components
  • Axios for HTTP requests
  • Vue Router for navigation

SPA (Telegram Mini App)

  • Composition API
  • Pinia stores
  • Tailwind CSS for styles
  • Telegram WebApp API
  • Vue Router

Naming Conventions

  • Classes: PascalCase (TelegramCustomerService)
  • Methods: camelCase (getCustomers)
  • Variables: camelCase ($customerData)
  • Constants: UPPER_SNAKE_CASE (MAX_RETRIES)
  • Files: PascalCase for classes, kebab-case for everything else
  • Tables: snake_case with acmeshop_ prefix

Error Handling

Always handle errors:

try {
    $result = $this->service->doSomething();
} catch (SpecificException $e) {
    $this->logger->error('Error message', ['exception' => $e]);
    throw new UserFriendlyException('User message');
}

Configuration

Use configuration files in configs/:

$config = $this->app->getConfigValue('app.setting_name');

Caching

Use Cache Service for caching:

$cache = $this->app->get(CacheInterface::class);
$value = $cache->get('key', function() {
    return expensiveOperation();
});