2

I am implementing a library that:

  1. Takes some input data (I put data structures and classes in the parsing package)
  2. Stores the data in a structure (I put data structures and classes in the storage package)
  3. Present the data in some meaningful way (chart package)

I divided my code this way because if I want to add another data representation (for example a table package) I can reuse parsing and storage packages.

I am though worried about circular dependencies:

  • My Parser should store input data in a dedicated data structure class, Record, this means that DataStorage has to know how to get data from it and that Parser should know how data insertion is performed in DataStorage class.
  • I am also concerned that the chart - and eventually table as well - package should know a bit more about how to get data from my DataStorage class.

Are my concerns valid? If yes, how could I solve the issue? I was thinking to expose some Interfaces in a contracts/interfaces package in such a way that specific classes such as Record and DataStorage would implement them and there wouldn't be a circular dependency anymore.

Would this solution be considered a good practice or are there any better alternatives?

5

I was thinking to expose some Interfaces in a contracts/interfaces package in such a way that specific classes such as Record and DataStorage would implement them and there wouldn't be a circular dependency anymore.

This is fine. Each package defines (in the more abstract way possible1) the inputs and outputs so the dependency upon these packages is implemented by dependency inversion. This is a clean dependency relationship between the package and consumers because both remain fairly decoupled from each other.

That said, package-by-feature is still sensible to the fact that, sometimes, we have cross-cutting concerns throttling between boundaries (packages, modules, layers, etc). The alternative then could be a different organization. Something similar to package-by-component2.


1: In line with this, it might interest the dependency rule: which data crosses the boundaries

2: For small boundaries such as libraries or small units of business package-by-feature works fine but when it comes to modular architectures, package-by-component makes a big deal. Think in small built-in features vs add-ons or plug-ins.

1

I think you should be careful about masking the cycle with interfaces.

I would recommend to let the storage know about the parser, but not the other way around. The parser seems to be your domain / business logic. Everything else revolves around it. Also see The Onion Architecture.

You can create a new package that orchestrates everything (e.g. app). But the transformation from "parser" to "storage" is probably very dependent on the storage system. So I think it belongs either in the "storage" package or in a "parserstorage" package.

When your parser generates so many data that it is from a performance perspective necessary to intertwine it with the storage, you can define interfaces for storage in the parser package and implement them in a "storage flavor" package.

0

Circular dependencies can be an indicator that the which-class-is-responsible-for of your architecture is not correct.

Usually you have

  • technical components (like csv-parser or html-table-display) and
  • business-domain-components (i.e. product-data) constisting of
    • data-items (the product values like name, description, price),
    • data-repositories (to save an load the product-data from/to csv (using your csv-parser) and/or database)
    • data-gui (edit, list (using your html-table-display), ... products in a form)
    • either services or mvc-models (i.e. load product-items from csv-repository do some processing and/or save to database-repository or display result)

This architecture avoids circular dependencies as

  • technical components have no knowledge about domain-components.
  • coordination between the classes are done in service and/or model

This Example whould be packaged into

  • two technical components
    • io (containing the csv-parser),
    • html-tools (containing the html-table tools))
  • and one business-domain-component
    • productdata (which you can load/save from csv/database and display as list on a web page)

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.