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:
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:
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:
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:
[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:
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
- Clean Architecture: Separating concerns made testing and maintenance easy
- TypeScript Everywhere: Catching errors at compile time saved hours
- PostgreSQL: Never had performance issues, even with complex queries
- Angular Signals: The new reactive system is a game-changer
What I'd Do Differently
- Start with Docker: Would have saved setup time for team members
- More automated testing: Integration tests would catch regressions earlier
- GraphQL instead of REST: Would reduce over-fetching on complex queries
- 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.
Need this built for your business?
I build bilingual (EN/ES) web apps and sites with .NET, Angular and AWS: production-ready, documented and handed off. See Full-Stack Development →
