2

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

asked Apr 1, 2024 at 11:56
2
  • 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? Commented 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? Commented Apr 1, 2024 at 17:09

2 Answers 2

5

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

answered Apr 1, 2024 at 17:26
5
  • Yep, I ended up with service locator too and then decided to ask. Thank you a lot Commented Apr 1, 2024 at 17:55
  • 2
    I 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. Commented 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 Commented Apr 1, 2024 at 18:46
  • @GregBurghardt sorry for wrong mention in prev comment Commented 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) Commented Apr 1, 2024 at 21:17
2

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.

answered Apr 1, 2024 at 15:28
1
  • 1
    Thanks, but I think I can not use collection. Updated question with more details Commented Apr 1, 2024 at 16:34

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.