Modules are uniquely identified by NSVCA which stands for name, stream, version, context, and architecture.
When creating a new module, you only need to decide about the name (name of your module, such as nodejs), and the stream (the flavour of your module, mostly a major version, such as 8).
If you want more details about the whole NSVCA, continue reading.
At the source level, modules are only identified by the first three: name, stream, and version.
Name of the module corresponds to the name of the application or the language stack it represents.
An example of a name could be postgresql for a PostgreSQL database module, or nodejs for a Node.js runtime.
Streams are variants of a module with a certain promise.
In most cases, streams promise backwards compatibility with a major version of the application or the language stack they provide. For example, let’s say the Node.js runtime is supported in two major versions: 6 and 8. In this case, the module nodejs would have two streams: 6 and 8.
However, streams can also promise different things such as stability. A good example of this is the calc package in Fedora which is maintained in two upstream branches: stable for the latest stable release and unstable for the latest development version. Using modularity, this package could be built as a calc module in two different streams: stable and unstable.
In addition to the version promise, streams are also a way for packagers to communicate the level of maintennance. Does the maintainer plan to apply every minor patch? Will they apply security fixes quickly? Or is the module updated only twice a year? This can also be part of the promise.
Other different example could be a stream that provides the software compiled using some experimental flags increasing the performance.
Anyway, you get the idea. Streams are very flexible and powerful tool. Use them wisely.
Building a module from one source can result in multiple different binaries. Different binaries are typically produced for different architectures (i.e. x86_64, armv7hl, etc.) and different Fedora releases (i.e. Fedora 28, Fedora 29, EPEL 7, etc.).
In addition to the name, stream, and version fields described above, there are two more for binaries:
Fedora is built for many different architectures. The architecture field simply distinguishes architecture-specific binaries from each other.
The value is typically the same as with RPM packages, i.e. x86_64, armv7hl, etc.
Context is used to distinguish binaries built for different Fedora releases. Thanks to stream expansion, modules can also be built against multiple streams of other modules, i.e. different versions of a language runtime etc.
The value is generated by the build system and is usually hidden from the user as it doesn’t have any informational value by itself — it is a hash. However, the client tooling consuming this valie can present it in a useful way.
One way of representing the context could be listing the Fedora releases for which a certain module has been built.