let's say I have the following aggregate:
class Road:
company_id: int # another aggregate
name: str
stops: List[Stop]
def add_stop(stop: Stop):
stops.append(stop)
class Stop:
precission: Precission
latitude: float
longitude: float
address: str
This is just a minified example. Let's also say that to create stops, I need to call a third party to geocode the object. Something like:
# this returns a correctly initialized Stop object
StopsDataAccess.get_by_address(address)
Now let's say that I want to add a stop to a road, so I would have an application service that would be similar to something like this:
class AddStopToRoadService:
def run(road_id, address):
road = RoadRepository.getById(road_id)
stop = StopsDataAccess.get_by_address(address)
road.add_stop(stop)
RoadRepository.save(road)
As it is, I do not see a lot of problems with this approach. The problem I have is when the following domain requirement comes:
- Only stops with the precision
EXACT
can be added to a route, those that areAPPROXIMATE
orUNKNOWN
cannot be added. Also, this can be changed by every company, so for instance, a different company can accept onlyAPPROXIMATE
or all of them.
So, to approach this I do the following:
class Road:
...
allowed_precission_types: List[Precissions]
...
def add_stop(stop: Stop):
if stop.precission not in self.allowed_precission_types:
raise StopNotAllowed()
stops.append(stop)
The allowed_precission_types
would be filled by the RoadRepository
. The problem I see with this approach is that that the list of allowed precision values is part of the Company
aggregate. So I'm not sure if it really fits in there. Am I breaking encapsulation? What would be the correct approach?