Building a Full-Stack Photography Management System with .NET Core and PostgreSQL
Development

Building a Full-Stack Photography Management System with .NET Core and PostgreSQL

Ricardo Gil
December 30, 2025
18 min read
#.NET Core #PostgreSQL #Angular #Full-Stack #Photography #Business Software

Introduction

Managing a photography business involves juggling clients, bookings, galleries, invoicing, and communication—all while trying to focus on what matters most: taking great photos. After working with several photography businesses, I noticed a common pain point: existing solutions were either too expensive, too complex, or lacked the specific features photographers needed.

That's why I built PhotoManagerAPI, a comprehensive full-stack solution using .NET Core 8, Angular 18, and PostgreSQL. In this post, I'll walk you through the architecture, key features, and technical decisions that make this system both powerful and maintainable.

The Problem: Why Build Custom Software?

Off-the-shelf photography management tools often fall short in several ways:

  • Generic workflows that don't match how photographers actually work
  • High monthly costs that eat into already thin margins
  • Limited customization when business needs change
  • Poor integration with existing tools and workflows
  • Data lock-in making it hard to switch providers
  • A custom solution addresses these issues while providing exactly the features needed—nothing more, nothing less.

    Architecture Overview

    The system follows a clean, modern architecture that separates concerns and enables independent scaling:

    Backend: .NET Core 8 Web API

    The API layer handles all business logic, data access, and security. Here's why .NET Core was the right choice:

  • Performance: Native async/await support and minimal overhead
  • Type safety: Strong typing catches errors at compile time
  • Rich ecosystem: NuGet packages for everything we need
  • Cross-platform: Deploy anywhere—Windows, Linux, or containers
  • Long-term support: Microsoft's commitment to LTS releases
  • Frontend: Angular 18

    Angular provides the structure needed for a complex business application:

  • Component architecture: Reusable, testable UI components
  • TypeScript: Type safety on the frontend too
  • Reactive forms: Complex validation made simple
  • RxJS: Powerful data flow management
  • Signals: The new reactive primitive for better performance
  • Database: PostgreSQL

    PostgreSQL handles all data persistence with excellent performance:

  • ACID compliance: Data integrity is non-negotiable
  • JSON support: Flexible data structures when needed
  • Full-text search: Built-in search without extra tools
  • Advanced indexing: Lightning-fast queries
  • Open source: No licensing costs or vendor lock-in
  • Core Features

    1. Client Management

    Track everything about your clients in one place:

    csharp
    public class Client
    {
        public Guid Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
        public string Phone { get; set; }
        public string Address { get; set; }
        public List<Booking> Bookings { get; set; }
        public List<Invoice> Invoices { get; set; }
        public DateTime CreatedAt { get; set; }
        public DateTime? LastContactedAt { get; set; }
    }

    The client model includes relationships to bookings and invoices, making it easy to see the complete history at a glance.

    2. Booking System

    Schedule shoots with conflict detection:

    csharp
    public async Task<BookingResult> CreateBookingAsync(CreateBookingDto dto)
    {
        // Check for scheduling conflicts
        var hasConflict = await _context.Bookings
            .AnyAsync(b => 
                b.PhotographerId == dto.PhotographerId &&
                b.Date == dto.Date &&
                b.Status != BookingStatus.Cancelled);
        
        if (hasConflict)
        {
            return BookingResult.Conflict("Time slot already booked");
        }
        
        var booking = _mapper.Map<Booking>(dto);
        booking.Status = BookingStatus.Pending;
        
        _context.Bookings.Add(booking);
        await _context.SaveChangesAsync();
        
        // Send confirmation email
        await _emailService.SendBookingConfirmationAsync(booking);
        
        return BookingResult.Success(booking);
    }

    3. Gallery Management

    Organize and share photos with clients:

  • Upload to AWS S3: Scalable, reliable storage
  • Image optimization: Automatic thumbnails and compression
  • Access control: Client-specific gallery passwords
  • Bulk operations: Upload, download, delete in batches
  • Expiration dates: Galleries auto-archive after X days
  • 4. Invoice Generation

    Create professional invoices with payment tracking:

    csharp
    public class Invoice
    {
        public Guid Id { get; set; }
        public Guid ClientId { get; set; }
        public string InvoiceNumber { get; set; }
        public DateTime IssueDate { get; set; }
        public DateTime DueDate { get; set; }
        public List<InvoiceItem> Items { get; set; }
        public decimal Subtotal { get; set; }
        public decimal Tax { get; set; }
        public decimal Total { get; set; }
        public InvoiceStatus Status { get; set; }
        public DateTime? PaidAt { get; set; }
    }

    5. Role-Based Access Control

    Different permissions for different team members:

  • Admin: Full access to everything
  • Photographer: Manage their own bookings and clients
  • Editor: Access galleries for post-processing
  • Client: View their own bookings and galleries
  • Implementation using ASP.NET Core Identity:

    csharp
    [Authorize(Roles = "Admin,Photographer")]
    [HttpPost("bookings")]
    public async Task<IActionResult> CreateBooking([FromBody] CreateBookingDto dto)
    {
        var result = await _bookingService.CreateBookingAsync(dto);
        return Ok(result);
    }

    [Authorize(Roles = "Admin")] [HttpDelete("users/{id}")] public async Task<IActionResult> DeleteUser(Guid id) { await _userService.DeleteUserAsync(id); return NoContent(); }

    Technical Highlights

    Performance Optimization

    I achieved significant performance improvements through:

    Database Indexing:

    sql
    CREATE INDEX idx_bookings_photographer_date 
    ON bookings(photographer_id, date)
    WHERE status != 'Cancelled';

    CREATE INDEX idx_invoices_client_status ON invoices(client_id, status);

    Query Optimization:

  • Used AsNoTracking() for read-only queries
  • Implemented pagination for large datasets
  • Added eager loading to prevent N+1 queries
  • Results:

  • 40% faster query response times
  • 60% reduction in database load
  • Smooth performance with 10,000+ records
  • Security Best Practices

    Security was a top priority throughout development:

  • JWT Authentication: Stateless, scalable auth
  • Password Hashing: BCrypt with high cost factor
  • SQL Injection Prevention: Parameterized queries
  • XSS Protection: Content Security Policy headers
  • CORS Configuration: Whitelist trusted origins only
  • Rate Limiting: Prevent brute force attacks
  • Deployment Strategy

    The application deploys in minutes using:

  • Docker containers: Consistent environments
  • Tailscale VPN: Secure remote access
  • PostgreSQL backup: Automated daily backups
  • SSL/TLS: Let's Encrypt certificates
  • Monitoring: Application Insights for telemetry
  • Lessons Learned

    What Worked Well

    1. Clean Architecture: Separating concerns made testing and maintenance easy 2. TypeScript Everywhere: Catching errors at compile time saved hours 3. PostgreSQL: Never had performance issues, even with complex queries 4. Angular Signals: The new reactive system is a game-changer

    What I'd Do Differently

    1. Start with Docker: Would have saved setup time for team members 2. More automated testing: Integration tests would catch regressions earlier 3. GraphQL instead of REST: Would reduce over-fetching on complex queries 4. Microservices for galleries: S3 operations could be a separate service

    Getting Started

    Want to build something similar? Here's my recommended approach:

    Phase 1: MVP (2-3 weeks)

  • Client CRUD operations
  • Basic booking system
  • Simple authentication
  • Phase 2: Core Features (3-4 weeks)

  • Gallery management with S3
  • Invoice generation
  • Email notifications
  • Phase 3: Polish (2-3 weeks)

  • Advanced search and filtering
  • Reporting and analytics
  • Mobile responsiveness
  • Tech Stack Recommendations

    For Small Teams (1-2 photographers):

  • Backend: .NET Core with SQLite
  • Frontend: Angular or React
  • Hosting: Single VPS or home server
  • For Growing Businesses (3-10 photographers):

  • Backend: .NET Core with PostgreSQL
  • Frontend: Angular with state management
  • Hosting: Docker containers on cloud VM
  • For Agencies (10+ photographers):

  • Backend: Microservices architecture
  • Frontend: Micro-frontends
  • Hosting: Kubernetes cluster
  • Conclusion

    Building PhotoManagerAPI taught me that custom software isn't just about writing code—it's about understanding the business problem and crafting a solution that fits perfectly. The combination of .NET Core, Angular, and PostgreSQL proved to be powerful, reliable, and maintainable.

    Whether you're building for photographers, consultants, or any service business, the principles remain the same: clean architecture, security first, and always optimize based on real-world usage data.

    Key Takeaways:

  • Choose proven technologies over trendy ones
  • Design your database schema carefully upfront
  • Security and performance can't be afterthoughts
  • User feedback drives the best features
  • Have questions about building a similar system? Feel free to reach out—I love talking about architecture and solving real business problems with code.

    Tech Stack:

  • .NET Core 8
  • Angular 18
  • PostgreSQL 16
  • AWS S3
  • Docker
  • Tailscale
  • Source Code: Available on GitHub (link in my portfolio)

    Related Posts

    Building with Angular + Firebase? Check out my Photography E-Commerce Platform that combines Firebase auth with Stripe payments and AWS S3 storage.

    Self-hosting your projects? Learn how I host production apps on a Raspberry Pi with PM2, Nginx, and Tailscale.

    About the Author: Ricardo Gil is a full-stack software engineer specializing in .NET/C#, Angular, and cloud platforms. Read more or subscribe for updates.

    📬Weekly Newsletter

    Get the best home lab & AI content

    No spam. One email per week. Unsubscribe anytime.

    Share this article