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

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?
    – Ewan
    Apr 1 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? Apr 1 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

5
  • Yep, I ended up with service locator too and then decided to ask. Thank you a lot Apr 1 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. Apr 1 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 Apr 1 at 18:46
  • @GregBurghardt sorry for wrong mention in prev comment Apr 1 at 19:03
  • mmm the more generic you go the more service locator like it becomes, until you have ThingFactory.Get(type,name)
    – Ewan
    Apr 1 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.

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

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.