I have a gen_server
in my cavv application that I need to start first to execute a call to. I want to use a command dispatcher for this. For a short example, this it the gen_server
's API:
a gen_server: cavv_user
-module(cavv_user).
-behavior(gen_server).
-define(SERVER(UserId), {via, gproc, {n, l, {?MODULE, UserId}}}).
start_link(UserId) ->
gen_server:start_link(?SERVER(UserId), ?MODULE, [UserId], []).
change_email_address(UserId, EmailAddress) ->
gen_server:call(?SERVER(AggregateId), {execute_command, #change_user_email_address{user_id=UserId, email_address=EmailAddress}}).
Before I can call cavv_user:change_email_address().
I need to start the cavv_user
. I do this is as a simple_one_for_one
child in a supervisor, like so:
a supervisor: cavv_user_sup
-module(cavv_user_sup).
-behaviour(supervisor).
-define(CHILD(ChildName, Type, Args), {ChildName, {ChildName, start_link, Args}, temporary, 5000, Type, [ChildName]}).
start_link() ->
supervisor:start_link({local, ?SERVER}, ?MODULE, []).
start_child(UserId) ->
supervisor:start_child(?SERVER, [UserId]).
init([]) ->
RestartStrategy = {simple_one_for_one, 1, 5},
Children = [?CHILD(cavv_user, worker, [])],
{ok, { RestartStrategy, Children} }.
The problem I am now facing is how to dispatch commands to a cavv_user
. I want to make sure the proper user is started first using start_child
, and then call the cavv_user:change_email_address().
I have found this anwser, to use a dispatcher: Erlang: what supervision tree should I end with writing a task scheduler?
So I created a command dispatcher and end up with a cavv_user_dispatcher
and a cavv_user_dispatcher_sup
that in turn contains the cavv_user_dispatcher
and the earlier cavv_user_sup
:
cavv_user_dispatch_sup
| |
cavv_user_dispatcher |
(gen_server) |
|
|
cavv_user_sup
| | |
cavv_user_1...cavv_user_N
The cavv_user_dispatcher
This works beautifully.
The problem I am facing now is, how do I properly write the code in cavv_user_dispatcher
? I am facing a problem with code duplication. How to properly call start_child and call the appropriate API of cavv_user?
Should I use some kind of Fun
like so?
-module(cavv_user_dispatcher).
dispatch_command(UserId, Fun) ->
gen_server:call(?SERVER, {dispatch_command, {UserId, Fun}}).
handle_call({dispatch_command, {UserId, Fun}}, _From, State) ->
cavv_user_sup:start_child(UserId),
Fun(), %% How to pass: cavv_user:change_email_address(..,..)?
{reply, ok, State};
Or duplicate the cavv_user
's API like so?
-module(cavv_user_dispatcher).
change_user_email_address(UserId, EmailAddress) ->
gen_server:call(?SERVER, {change_user_email_address, {UserId, EmailAddress}}).
handle_call({change_user_email_address, {UserId, EmailAddress}}, _From, State) ->
cavv_user_sup:start_child(UserId),
cavv_user:change_email_address(UserId, EmailAddress),
{reply, ok, State};
Or should I re-use the command records from cavv_user
into some kind of util to properly build them and pass them around? Maybe some better way to pass the function I want to call at cavv_user
?
I would like to solve the problem in the best Erlang way as possible, without code duplication.