Let's say I have a tree(composite pattern) of nodes,every node is a process that can contain other nodes/processes.
Tree constructed with UI where user can create node that copy one row of data from one database to another, node taking as parameters a source database and a destination database names.
Moreover, user can create replica-like node, where one row from one database must be copied to several oltp, olap and key-value storages by names.
Composition of nodes is unknown
Those nodes can be children of any other node at any level
Process may be any process even request to googlesheets api for update row
When tree constructed it must be run
So, I take data tree and try convert it to process tree. Walking through data tree I meet many dependencies that must be resolved, for example, names of storages to its connections to construct runable replica-node.
Resolving function smells:
type resolve func(
data,
dep1,
db1,
db2,
db3 any,
// and so on
)
every new dependecy must change function's interface.
Data example
[
{"$proc": "proc1"},
{
"$proc": "proc2",
"dependency": "value"
},
{
"$proc": "proc3",
"children" : [
{"$proc": "proc1"},
{
"$proc": "proc4",
"param": 1,
"dependencies": ["dep1","dep2"]
},
{
"$proc": "replica-node",
"row_id": 42,
"source": "mydb1",
"destinations": [
"mydb2",
"mydb3",
"mydb4"
]
},
{
"$proc": "replica-node",
"row_id": 51,
"source": "mydb10",
"destinations": [
"mydb20",
"mydb30",
"mydb40"
]
}
]
}
]
Is there any better way to solve this issue?
Thanks!
UPDATED
Full tree runs at once because parent process can finish its job only after all children are done.
Children run async.
Db is not about only databases, it is any connection to db, maybe settings for external api call, kafka connection and so on.
For example, after I copied data from postgres to redis and copied data between googlesheets and mysql, I need send notification about it to email.
[
{
"$proc": "send email after children done",
"to": "email",
"text": "some text",
"provider": "mailsender",
"children": [
{
"$proc": "copy from postgres to redis",
"redis_name": "my_redis",
"postgres_name": "my_pg"
},
{
"$proc": "copy from postgres to redis",
"redis_name": "my_ANOTHER_redis",
"postgres_name": "my_ANOTHER_pg"
},
{
"$proc": "copy from googlesheet to mysql",
"googlesheet": "my_googlesheet_id",
"mysql": "my_mysql"
}
]
}
]
So I need pgconnection, redisconnection and mailsender dependencies.
All those dependencies have defferent interfaces.
Also I can not use collection because two postgres connections has one interface, but different configs(names),
and when user create process that copy data from one postgres to another postgres, I need determine what is source and what is destination.
And collection can not guarantee that it contain all dependencies presented in a tree
-
Im unclear if your dependencies are actual service dependencies or dynamic construction of objects with options. You seem to have two "proc1"s with different dependencies?Ewan– Ewan2024年04月01日 17:04:18 +00:00Commented Apr 1, 2024 at 17:04
-
This question is very unclear - it would help if you provided some more context. From what I was able to gather, you're saying that the input data (the example you provided) is created by users through a UI, is unknown in advance, there's nesting involved, and that you need to turn that input into some sort of a data structure that you can work with. However, the input seems non-uniform (fields don't have the same names, the structure is not the same throughout for different cases), so it's hard to parse in an abstract/generic way, so your resolve function is tied to a specific use case?Filip Milovanović– Filip Milovanović2024年04月01日 17:09:28 +00:00Commented Apr 1, 2024 at 17:09
2 Answers 2
OK I think I see what you mean.
You have your CopyFromGoogleToMysql
class. it has two dependencies IMysql
and IGoogle
which are passed in via constructor injection, but the connection string for each is dynamic.
Here its difficult to use standard DI frameworks you would have to recurse through the dependencies creating and registering named dependencies, and then register the CopyFromGoogleToMysql
linking it to the correct named dependencies. It will be a pain.
Instead I would create factory classes. ie IMysqlFactory GetMySqlDependency(string connstr)
You can now inject the same factory into everything that needs to connect to mysql and it can grab its dependency when needed.
You could go further and make a IDatabaseFactory GetDatabase(string type, string connstr)
if you wanted and the methods matched.
This starts to look a lot like a service locator pattern, which is not normally recommended. But in your case might be required, due to your "connect to any connstr" functionality
-
Yep, I ended up with service locator too and then decided to ask. Thank you a lotАртем Антонов– Артем Антонов2024年04月01日 17:55:27 +00:00Commented Apr 1, 2024 at 17:55
-
2I don't think this is necessarily a "service locator," because it is very specific. A service locator is just a big, general bag of stuff, but factories aren't "general bags of stuff." This is a perfectly sound solution without needing to invoke the name of an anti-pattern.Greg Burghardt– Greg Burghardt2024年04月01日 18:17:25 +00:00Commented Apr 1, 2024 at 18:17
-
@greg-burghardt The thing is what I need pass as params all factories, but not all of them will be used due running. For example if tree consists from one node without dependencies. And every time when new type of connection-dependency appear in tree, resolver interface will expand parameters list to accept new factory, what I tried to escapeАртем Антонов– Артем Антонов2024年04月01日 18:46:12 +00:00Commented Apr 1, 2024 at 18:46
-
@GregBurghardt sorry for wrong mention in prev commentАртем Антонов– Артем Антонов2024年04月01日 19:03:55 +00:00Commented Apr 1, 2024 at 19:03
-
mmm the more generic you go the more service locator like it becomes, until you have ThingFactory.Get(type,name)Ewan– Ewan2024年04月01日 21:17:15 +00:00Commented Apr 1, 2024 at 21:17
every new dependecy must change function's interface.
What? Why? Replace DB1, DB2, etc with DBs collection.
And remember, collections can be empty. That’s a good thing.
Also, are you sure you need to resolve this much with a single call? Recursive tree structures don’t insist you build the whole tree at once. Take it a node at a time.
-
1Thanks, but I think I can not use collection. Updated question with more detailsАртем Антонов– Артем Антонов2024年04月01日 16:34:09 +00:00Commented Apr 1, 2024 at 16:34