0

I am working on a project that generates an API with the possibility of doing CRUD operations based on a high-level description of the resources that the user would like to have in an application. In the process of generating the code, multiple steps have to be taken (for some context, I am generating Python code from Python with Jinja2):

  1. Generating code that handles the connection with the database
  2. Generating Dockerfiles
  3. Generating Pydantic Models for the input resources
  4. Generating classes that link the application to a database using SQLAlchemy models
  5. Generating methods that facilitate CRUD operations on the database's tables
  6. Generating the API code

Almost all of the operations depend on the input from the user but are not directly linked in any way, so they could even be executed in parallel.

Researching for ways of how to properly handle sequential operations I have found the Chain of Responsibility pattern, but it does not really apply in my situation as I have to execute all of the steps regardless of the user input (as long as it's a valid input).

I have also thought of something like the Strategy pattern for each generation process, but I'm having a hard time deciding if any of these patterns is a right fit for my situation.

Currently I have structured my code in the following way:

  1. I received the input and check for several conditions to be met (validations)
  2. I process the input and change it dynamically to adjust it for the following steps
  3. I execute the following steps in a procedural manner, one after the other.
r = RelationshipHandler(Input(**res).resources)
r.execute()
resources = [resource.dict() for resource in r.resources]
generate_connection()
generate_db_create_code(resources)
generate_docker_compose()
generate_pydantic_models(resources)
generate_sqlalchemy_classes(resources)
generate_model_code(resources)
generate_fastapi_code(resources)

All of these operations are executed by an orchestator/choreographer - which would basically imply that I'm at least using an extremely simple version of the Choreography pattern (more common in the context of microservices).

My question is: how are situations like this handled in general? What patterns or best practices would make the code be more clean/readable/logical and easier to maintain?

I am aware that there might be multiple ways of handling situations like the described one, I am just trying to find other, more elevated ways of solving the same problem.

2 Answers 2

5

There is nothing inherently wrong in having an orchestator doing these steps one after another. When all you need is this fixed step of sequences, don't start to throw some fancy patterns at the code base, this will only overcomplicate things for no benefit.

The key point, however, is, to make the input and output of each of the steps explicit instead of using "global state" for passing the data around.

For example:

conn=generate_connection()
my_code = generate_db_create_code(conn,resources)
generate_docker_compose(my_code)

(The above input/output might not be correct, but I guess you get the idea).

The goals behind this are the following:

  • it should become more self explanatory what each step does, and

  • each step should be unit testable isolation, and

  • it becomes less error prone when you have to add or reorder some steps.

Those are typical goals you should have in mind for this design, not some patterns you have heard from.

4
  • I honestly think that this is the right answer, but my inner "engineer" wished to find more flexible/scalable/generic solutions. Maybe that this is not the right place to ask questions that do not have a straight or standard answer, as it seems questions like this one get downvoted. Apr 26 at 7:05
  • 1
    @Ionut-AlexandruBaltariu: questions about applying "patterns or best practices" are often downvoted here because those questions typically assume just applying some "patterns or best practices" automatically leads to "better" code. Unfortunately, applying patterns with no need leads to exact the opposite - overengineered (and hence hard to maintain) solutions. This would not change by asking the same question at a different place.
    – Doc Brown
    Apr 26 at 12:11
  • This behavior is, however, inconsistent. Some questions are put exactly in the same manner and they seem to be taken serious. Apr 27 at 8:31
  • 1
    @Ionut-AlexandruBaltariu: you are dealing with a community of individuals here. And what you call "same manner" might be seen differently by others, especially when looking at the details. For example, lots of questions which were accepted before ~2016 are not accepted any more since the site's focus (and name) was changed at that time,
    – Doc Brown
    Apr 27 at 9:37
0

If these steps are independent, one architecture you can use if the event driven architecture which is implemented using the pub/sub design pattern. Basically one class (the publisher) raise or fires an event "generateCode", and many consumer classes (subscribers) consume that event by running a certain piece of code.

5
  • This would be great if the code generation process would be something continuous in time. But it only happens once, the whole codebase is generated based on the input from the user. Apr 25 at 21:01
  • Didnt fully get what you mean by continuous in time. Once you have the user input raise the event. Note here you can have arguments and properties for that event in which you can pass the user input to the consumers.
    – EEAH
    Apr 25 at 21:08
  • Also one goal of the event driven architecture is that its a background job that can be done by another host or machinr that shares the same database where the events are stored. If you dont need that you can create a custom service that can run multiple classes in parallel to simulate the architecture
    – EEAH
    Apr 25 at 21:12
  • After second tough you dont need a custom service if the development or business needs doesnt require this. You can use the parallelism provided by the programming language
    – EEAH
    Apr 25 at 21:16
  • I wish to build an application that is ran once with a certain combination of configuration parameters and that delivers the whole codebase as an answer. Another generation of could would mean running the application again. This event driven approach would definitely make sense in a distributed application, but that is not the case. Apr 26 at 7:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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