Mafinar Khan ● Planswell ● @mafinar ● /code-shoily
#core #missionStatement #elevatorPitch
#backOffice #helper #internal
#registration #userFacing #external
#tripManagement #userFacing #driverFacing #initiation
#tripManagement #userFacing #driverFacing #GIS
#tripManagement #userFacing #driverFacing #GIS
#legal #authr #user #driver #staff
- Domain Expert
Manage Drivers
Manage Vehicles
Register User
User Requests Trips
Driver Accepts Trips
Track Trips
Match Driver with Rider
Trip Status Control
Manage Location
Authenticate Rider
Authorize Trip
Manage Drivers
Manage Vehicles
Register Rider
User Requests Trips
Driver Accepts Trips
Track Trips
Match Driver with Rider
Trip Status Control
Manage Location
Authenticate Rider
Managing Rider
User
Trip
Driver
Places
Manage Address
from infrastructure.exceptions import DomainValidationError
from infrastructure.services import validate_email
from typing import List
from dataclasses import dataclass
from .value_objects import Vehicle, DriversLicense
from .services import validate_drivers_license
@dataclass
class Driver:
id: str
first_name: str
last_name: str
email: str
phone_number: str
mugshot: str
username: str
date_joined: str
last_login: str
drivers_license: DriversLicense
vehicles: List[Vehicle]
def __post_init__(self):
if not validate_email(self.email):
raise DomainValidationError("Invalid Email")
if not validate_drivers_license(self.drivers_license):
raise DomainValidationError("Invalid Drivers License")
from typing import List
from dataclasses import dataclass
from infrastructure.exceptions import DomainValidationError
from .services import create_default_geo_info, validate_latlng
@dataclass(frozen=True)
class GISInfo:
bounding_box: List[float]
projection: List[float]
@dataclass(frozen=True)
class Location:
lat: float
lng: float
geo: GISInfo
def __post_init__(self):
self.geo = create_default_geo_info()
if not validate_latlng(self.lat, self.lng, validate_latlng):
raise DomainValidationError("Invalid lat/lng")
import datetime
from typing import List
from dataclasses import dataclass
from infrastructure.exceptions import DomainValidationError
from .value_objects import (Driver, Rider, Address, Location, TripTime, TripStatus)
@dataclass
class Trip:
id: str
start_time: TripTime
end_time: TripTime
driver: Driver
rider: Rider
trip_status: TripStatus("Requested")
start_location: Address
end_location: Address
locations: List[Location]
responses: List[Driver]
def validate_time_range(self):
now = datetime.datetime.now()
if now > self.start_time or (self.end_time > self.start_time):
return False
return True
def __post_init__(self):
if not self.validate_time_range():
raise DomainValidationError("Time range error")
from .entities import *
from .repository import create_trip, update_trip, find_trips_by_rider, find_trips_by_driver
def request_trip(rider_id: str) -> Trip:
return create_trip(rider_id)
def cancel_trip(canceller_id: str, cancelled_by: str, reason) -> None:
if cancelled_by == "driver":
return update_trip(driver=canceller_id, reason=reason)
else:
return update_trip(rider=canceller_id, reason=reason)
def trip_history_for_rider(id: str, start_time: datetime, end_time: datetime) -> Trip:
return find_trips_by_rider(rider=id, start_time=start_time, end_time=end_time)
def trip_history_for_driver(id: str, start_time: datetime, end_time: datetime) -> Trip:
return find_trips_by_rider(driver=id, start_time=start_time, end_time=end_time)
from typing import List
from django.contrib.auth.models import User
from infrastructure.models import Driver, Vehicle
from acl import convert_to_driver
def create_driver(driver_info,
user_info,
vehicles) -> Driver:
user = User.objects.create(**user_info)
driver = Driver.objects.create(user=user, **driver_info)
for vehicle in vehicles:
Vehicle.objects.create(driver=driver, **vehicle)
convert_to_driver(driver)
def search_driver(**criteria) -> List[Driver]:
matched_drivers = Driver.objects.filter(**criteria)
return [convert_to_driver(driver) for driver in matched_drivers]
Title | Filename | Description |
---|---|---|
Entities | entities.py | Entity Dataclass |
Value Objects | value_objects.py | Value object dataclasses |
Aggregates | entities.py | - |
Services | services.py | Service functions |
Repository | repository.py | Repository class |
Events | events.py | Signal handlers |
Folder/Module | Description |
---|---|
application | "View" side of things: web, api etc. May include typical Django apps (sans model) |
domain | Home of all the bounded contexts |
infrastructure | All the Django models and migrations (WHY?), along with any bash/python scripts, python libraries etc |
import json
from django.http import HttpResponse
from django.contrib.auth.decorators import login_required
from django.shortcuts import render
from domain.trips.services import find_trips_by_driver, get_current_trip
from domain.permissions.services import can_view_trips
@login_required
def get_driver_trips(request, driver_id):
trip = None
if can_view_trips(request.user, driver_id, driver=True):
start_time = request.GET.get("start_time")
end_time = request.GET.get("end_time")
trip_dict = find_trips_by_driver(driver_id, start_time, end_time, asdict=True)
return render(request, "trips/trip_report.html", {trips: trip_dict})
@login_required
def cancel_trip(request):
cancellation_status = None
if can_cancel_trip(trip, request.user):
cancellation_status = user_cancel_trip(trip)
return HttpResponse(json.dumps(cancellation_status), status=cancellation_status or 403)
"Architecture Patterns in Python"
Didn't know this book was written while I was presenting this. It explains a lot of things I said here a lot more comprehensively. If you're interested in Python + DDD, I would recommend taking a look at the book.