Industries: Automative and Transporation

Building a Robust Admin Panel with Symfony, Doctrine & Refine

The project centered on the largest online marketplace in Finland for cars, motorcycles, boats, real estate, and related services. The platform supports millions of listings and serves as a critical hub for both consumers and dealers across multiple verticals.

As the business expanded, the need for a unified, modern, and secure backend architecture became urgent. Internal teams required an admin interface capable of handling high-volume data management, complex validations, and cross-vertical scalability.

Key Challenges

  • Unsafe direct database access: Some controllers or legacy scripts used raw SQL queries or bypassed the domain layer, making changes risky and inconsistent.
  • Lack of centralized validation: Business rules (e.g. pricing constraints, entity integrity) were duplicated in different layers (controller, forms, front-end).
  • Complex admin workflows: The admin panel needed CRUD for various entities (cars, users, listings, categories, pricing rules, etc.), with filtering, bulk operations, role-based access, and audit logs.
  • Scaling across verticals: As new verticals (e.g. motorcycles, commercial vehicles) came online, the platform needed to share logic while allowing customization.
  • Performance & maintainability: Frequent DB load, N+1 query problems, and debugging issues in data access logic.

Solution / Approach

  1. Clean Architecture with Symfony + Doctrine
    • Each entity has a repository class; all data access goes through repositories. We disallowed raw SQL in controllers or services, except in exceptional cases with encapsulation.
    • We used Doctrine’s QueryBuilder / DQL / custom repository methods for optimized data fetching, with careful join strategies and pagination.
    • Domain logic (business rules) is kept in service / domain layer rather than in controllers.
  2. Refine-based Admin Panel
    • We built the admin panel using Refine (a React-based framework for building admin UIs). Refine’s abstractions (data providers, resource definitions, form components) let us declaratively define entity CRUD, filters, lists, relationships, etc.
    • On the backend, we created REST / JSON endpoints that map to repository/service operations, enforcing validation and business rules securely.
    • No direct DB access is exposed to the frontend; all operations go through controlled APIs.
  3. Unified Validation Strategy
    • We defined validation constraints (Symfony Validator) in entity / DTO classes (e.g. @Assert\NotBlank, custom validators) and in form DTOs used by APIs.
    • We enforced validations in service layer before persisting, returning structured errors to the admin UI.
    • This centralization prevented discrepancies between form-level validation and domain logic.
  4. Role-Based Access & Audit
    • Admin users have roles/permissions (e.g. ROLE_ADMIN, ROLE_MODERATOR, ROLE_SUPER). Endpoints and UI features are guarded accordingly.
    • All entity changes are logged in an audit table (who changed what, when). This was built with Doctrine event subscribers/listeners capturing preUpdate / prePersist and storing changes.
  5. Performance Optimizations
    • For listing pages, we avoided N+1 query problems by eager-loading needed relations (via join / select) only where necessary.
    • We implemented caching (Doctrine query result caching or Redis) for rarely changed reference data (e.g. categories, configurations).
    • For bulk operations (mass edits, deletes), we used batched queries and queued processing when appropriate.
  6. Testing & Quality Assurance
    • We built unit tests for services, repository logic, and validation.
    • Integration tests covered API endpoints and end-to-end flows.
    • We also introduced test fixtures and seed data for admin scenarios.
    • Monitoring and error logging helped catch anomalies early in staging.

Results / Impact

  • Zero direct DB incidents in production (i.e. no unmediated SQL manipulations) after rollout.
  • Faster feature delivery: new verticals could reuse the same admin scaffolding, reducing overhead by ~30–40%.
  • Fewer bugs: validation mismatches dropped by 80%, because we centralized logic.
  • Better maintainability: onboarding new developers was smoother thanks to clear repository/service/form layers.
  • Performance improvements: list page response times dropped (e.g. from 500ms to 150ms) after refactoring queries and caching.
  • Admin users reported a more consistent, responsive interface with less error states.

Lessons Learned & Best Practices

  • Don’t bypass your domain / service layer — always funnel logic through a controlled path.
  • Centralize validation — having constraints in one place avoids drift.
  • When using an admin UI framework (Refine), invest time modeling your resources well (which fields, filters, relations) for best UX.
  • Watch for N+1 pitfalls early; profile queries as you build list pages.
  • Audit logging is invaluable for tracing back issues and for accountability.
  • Be prepared to refactor: you will discover new edge cases as the system grows.

Conclusion & Future Directions
With this new architecture, our platform is better positioned for future expansion: new verticals (e.g. motorbikes, trucks), API-first capabilities (for mobile apps or partner integrations), and potential migration to microservices if needed. Going forward, we plan to explore GraphQL endpoints for admin, event sourcing for complex business flows, and further caching layers or CQRS for high-throughput segments.

Contact Us

Let us tailor a service package that meets your needs. Tell us about your business, and we will get back to you with some ideas as soon as possible!

Have a question?

Thank you! Your request has been successfully sent.
Oops! Something went wrong while submitting the form.