# Django Project Architecture Plan ## Overview This project is a modular Django-based project management and business operations platform with: - Role-based access control - Project management - Private kanban workflows - Issue tracking - Ticketing system - Finance & payroll - Activity tracking - HTMX + Alpine.js frontend architecture --- # Project Structure ````text myproject/ ├── config/ # settings, urls, wsgi ├── apps/ │ ├── accounts/ # CustomUser, role system │ ├── projects/ # Project, Membership │ ├── finance/ # Invoice, Payment, Expense, Payroll │ ├── kanban/ # Board, Column, Card (private per user) │ ├── issues/ # Issue, Comment, Label, Status │ ├── tickets/ # Ticket, FeatureRequest │ └── activity/ # ActivityLog, progress aggregation ├── templates/ │ ├── base.html │ ├── partials/ # HTMX partial templates │ └── components/ # Alpine.js UI components └── static/ ``` --- # Actors ## Admin - Full system access - Manage users, projects, finance, tickets, and reports - Read-only access to aggregated kanban progress ## Client - View own projects - Create/view tickets - View invoices and payments - Comment on issues ## Team Member - Work on assigned issues - Access own kanban board - View assigned projects - Access payroll information --- # Django Apps Breakdown ## 1. accounts ### Responsibilities - Authentication - Authorization - Role management - Project memberships ### Main Models ```python class CustomUser(AbstractUser): ROLE_CHOICES = [ ("admin", "Admin"), ("client", "Client"), ("member", "Team Member"), ] role = models.CharField(max_length=20, choices=ROLE_CHOICES) ``` ```python class ProjectMembership(models.Model): user = models.ForeignKey(CustomUser, on_delete=models.CASCADE) project = models.ForeignKey("projects.Project", on_delete=models.CASCADE) joined_at = models.DateTimeField(auto_now_add=True) ``` --- ## 2. projects ### Responsibilities - Core project management - Team assignment - Project lifecycle ### Main Models ```python class Project(models.Model): class Status(models.TextChoices): PLANNING = "planning" ACTIVE = "active" REVIEW = "review" DONE = "done" name = models.CharField(max_length=255) client = models.ForeignKey( "accounts.CustomUser", on_delete=models.CASCADE, related_name="client_projects" ) status = models.CharField( max_length=20, choices=Status.choices, default=Status.PLANNING ) members = models.ManyToManyField( "accounts.CustomUser", through="accounts.ProjectMembership" ) ``` --- ## 3. finance ### Responsibilities - Invoices - Payments - Expenses - Payroll management ### Main Models ## Invoice ```python class Invoice(models.Model): class Status(models.TextChoices): PENDING = "pending" PAID = "paid" OVERDUE = "overdue" project = models.ForeignKey("projects.Project", on_delete=models.CASCADE) client = models.ForeignKey("accounts.CustomUser", on_delete=models.CASCADE) amount = models.DecimalField(max_digits=10, decimal_places=2) due_date = models.DateField() status = models.CharField(max_length=20, choices=Status.choices) ``` ## Payment ```python class Payment(models.Model): invoice = models.ForeignKey(Invoice, on_delete=models.CASCADE) paid_at = models.DateTimeField() method = models.CharField(max_length=100) ``` ## Expense ```python class Expense(models.Model): project = models.ForeignKey("projects.Project", on_delete=models.CASCADE) raised_by = models.ForeignKey("accounts.CustomUser", on_delete=models.CASCADE) amount = models.DecimalField(max_digits=10, decimal_places=2) category = models.CharField(max_length=100) ``` ## TeamPayment ```python class TeamPayment(models.Model): member = models.ForeignKey("accounts.CustomUser", on_delete=models.CASCADE) period = models.DateField() amount = models.DecimalField(max_digits=10, decimal_places=2) ``` --- ## 4. kanban ### Responsibilities - Personal task management - Progress tracking - Drag-and-drop workflow ### Privacy Rules - Each board belongs to one user - Members cannot access others' boards - Admin sees only aggregated progress ### Main Models ## Board ```python class Board(models.Model): owner = models.ForeignKey( "accounts.CustomUser", on_delete=models.CASCADE ) project = models.ForeignKey( "projects.Project", null=True, blank=True, on_delete=models.CASCADE ) ``` ## Column ```python class Column(models.Model): board = models.ForeignKey(Board, on_delete=models.CASCADE) name = models.CharField(max_length=100) order = models.PositiveIntegerField() ``` ## Card ```python class Card(models.Model): column = models.ForeignKey(Column, on_delete=models.CASCADE) title = models.CharField(max_length=255) description = models.TextField(blank=True) due_date = models.DateField(null=True, blank=True) order = models.PositiveIntegerField() ``` ### Secure QuerySet Pattern ```python class BoardQuerySet(models.QuerySet): def for_user(self, user): return self.filter(owner=user) ``` --- ## 5. issues ### Responsibilities - Bug tracking - Feature tracking - Task assignment - Internal discussion ### Main Models ## Issue ```python class Issue(models.Model): class Status(models.TextChoices): OPEN = "open" IN_PROGRESS = "in_progress" RESOLVED = "resolved" CLOSED = "closed" class Priority(models.TextChoices): LOW = "low" MEDIUM = "medium" HIGH = "high" project = models.ForeignKey("projects.Project", on_delete=models.CASCADE) reporter = models.ForeignKey( "accounts.CustomUser", on_delete=models.CASCADE, related_name="reported_issues" ) assignee = models.ForeignKey( "accounts.CustomUser", null=True, blank=True, on_delete=models.SET_NULL, related_name="assigned_issues" ) status = models.CharField(max_length=20, choices=Status.choices) priority = models.CharField(max_length=20, choices=Priority.choices) ``` ## IssueComment ```python class IssueComment(models.Model): issue = models.ForeignKey(Issue, on_delete=models.CASCADE) author = models.ForeignKey("accounts.CustomUser", on_delete=models.CASCADE) body = models.TextField() ``` --- ## 6. tickets ### Responsibilities - Client support - Feature requests - Bug reporting ### Main Models ```python class Ticket(models.Model): class TicketType(models.TextChoices): BUG = "bug" FEATURE = "feature_request" SUPPORT = "support" class Status(models.TextChoices): NEW = "new" ACKNOWLEDGED = "acknowledged" IN_PROGRESS = "in_progress" DONE = "done" REJECTED = "rejected" creator = models.ForeignKey( "accounts.CustomUser", on_delete=models.CASCADE ) project = models.ForeignKey( "projects.Project", null=True, blank=True, on_delete=models.CASCADE ) ticket_type = models.CharField(max_length=30, choices=TicketType.choices) status = models.CharField(max_length=30, choices=Status.choices) ``` ### Access Rules - Only admin and client can create tickets - Team members can only view assigned tickets - Tickets may be converted into issues --- ## 7. activity ### Responsibilities - Audit logging - Timeline tracking - Progress aggregation ### Main Models ```python class ActivityLog(models.Model): actor = models.ForeignKey( "accounts.CustomUser", on_delete=models.CASCADE ) verb = models.CharField(max_length=255) content_type = models.ForeignKey( ContentType, on_delete=models.CASCADE ) object_id = models.PositiveIntegerField() timestamp = models.DateTimeField(auto_now_add=True) ``` ### Rules - Members cannot view other members' activity logs - Admin dashboard uses aggregated activity metrics --- # Frontend Stack ## HTMX Use HTMX for: - Kanban drag-and-drop persistence - Ticket status updates - Issue comments - Live search - Partial rendering - Inline updates ## Alpine.js Use Alpine.js for: - Modal state - Dropdowns - Tabs - Confirm dialogs - Inline edit toggles ## Suggested Drag-and-Drop Pattern - Use Sortable.js for drag UI - Trigger HTMX POST request on drag end - Persist new ordering server-side --- # Permission Pattern Instead of Django object permissions, use reusable helper functions and view mixins. ## Example ```python class ProjectMemberRequired(LoginRequiredMixin): def dispatch(self, request, *args, **kwargs): project = get_object_or_404( Project, pk=kwargs["pk"] ) if not project.members.filter( pk=request.user.pk ).exists(): raise PermissionDenied return super().dispatch(request, *args, **kwargs) ``` --- # Access Control Matrix | Module | Admin | Client | Team Member | |------------------|----------------|----------------------|---------------------| | Kanban Board | Progress only | No access | Own board only | | Issues | Full | View + comment | Assigned only | | Tickets/Features | Full | Create + view own | View assigned | | Finance | Full | Own invoices | Own payroll | | Projects | Full | View own project | Assigned project | --- # Suggested Folder Layout ```text core/ ├── accounts ├── projects ├── notifications └── activity features/ ├── kanban ├── issues └── tickets finance/ ├── invoices ├── payments ├── expenses └── payroll templates/ ├── base ├── components ├── partials └── pages ``` --- # Recommended Tech Stack ## Backend - Django - Django REST Framework (optional API layer) - PostgreSQL - Redis - Celery ## Frontend - HTMX - Alpine.js - Tailwind CSS - django-tailwind-cli ## Infrastructure - Nginx - Gunicorn - Docker - GitHub Actions CI/CD --- # Future Improvements - Real-time notifications with Django Channels - WebSocket activity feed - Time tracking - Project analytics dashboard - File attachments - Email automation - AI-assisted issue summaries - Multi-tenant architecture --- # Final Notes This architecture keeps: - Apps modular - Permissions predictable - HTMX interactions clean - Kanban privacy secure - Finance isolated - Features scalable The structure is optimized for: - Long-term maintainability - Team collaboration - Incremental feature expansion - Clear domain separation ````