-
Notifications
You must be signed in to change notification settings - Fork 43
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Application: Alternative design for Interactors #67
Comments
Example of problems mentioned in (2):
The most common solutions ( |
What can be done:
|
Ok, let's take Sobolev's example to formulate expectations about Interactor (aka Use Case):
class AddMember(Interactor):
@in
class Request:
team_id: str
member_id: str
@out
class Reply:
success: bool
team_repo: Repository = Inject(qualifier=Team)
user_repo: UserRepository = Inject()
service: AuthorizationService = Inject()
process = combine(
(validate_input, bail_out),
(get_team, bail_out),
(get_user, take_current_user),
(add_user_to_team, bail_out),
(send_notification, ignore_errors),
) |
What can be done (cont'd):
class AddMember(Interactor):
@in
class Request:
team_id: str
member_id: str
@out
class Reply:
success: bool
request: Request = Inject()
team_repo: Repository = Inject(qualifier=Team)
user_repo: UserRepository = Inject()
service: AuthorizationService = Inject()
process = combine(
(validate_input, bail_out),
(get_team, bail_out),
(get_user, take_current_user),
(add_user_to_team, bail_out),
(send_notification, ignore_errors),
)
__call__ = process |
Interactor as process orchestrator vs. immutabilityProcess OrchestratorYou might think that class AddMember(Interactor):
...
request: Request = Inject()
team_repo: Repository = Inject(qualifier=Team)
user_repo: UserRepository = Inject()
service: AuthorizationService = Inject()
team: Team
user: User
process = combine(
(validate_input, bail_out),
(get_team, bail_out),
(get_user, take_current_user),
(add_user_to_team, bail_out),
(send_notification, ignore_errors),
) So when def add_member_to_team(interactor: AddMember):
interactor.team.append(interactor.user) It has the advantage that ImmutabilityIt's great to have key fragments of logic made pure and with immutable arguments. All you have to test is the mapping between its input and output. But this means that there is a def add_member_to_team(interactor: AddMember, state: AddMemberState):
state['team'].append(state['team']) The interactor instance can't serve as an immutable copy of the state: creating another instance of the interactor would reset DI instances on What a choice... |
Ok, let's keep it simple. Imagine we have a function defining interaction logic. We want to make it:
... then you can do it just by Example 1 (no composition within
|
An interaction logic function defined outside the interactor. Example 2 (no composition, external function)# pure_domain_logic.py
@success
@inject
def add_member(
interactor: Interactor,
team_repo: Repository = Inject(qualifier=Team),
**kwargs) -> Union[Success, Failure]:
...
@add_member.on_failure
def _(interactor: Interactor, **kwargs)
return ...
# my_application_logic.py
class AddMember(Interactor):
...
request: Request = Inject()
presenter: Presenter = Inject(qualifier='AddMember')
user_repo: UserRepository = Inject()
process = add_member NB:
|
The reason behind all this stuff: Railway-Oriented Programming |
This creates a dependency from the domain layer to the application. It should be the other way around. |
Current implementations of
Interactor
pattern (#4, #51) has several flaws to consider:InputPort
andRequestModel
. Doesn't generalize enough about sources of data, i.e. a model got from a database repository is treated differently than the data got from request.InputPort
: a generic interface of theRequestModel
doesn't have such nicedataclass
features as domain objects got from anyRepository
.The text was updated successfully, but these errors were encountered: