> ## Content Index
> Fetch the complete content index at: https://blog.aronis.de/llms.txt
> Use this file to discover other available public pages before exploring further.

# Inheriting Lifecycle Data the Smart Way: From Applications to IT Components
- URL: https://blog.aronis.de/inheriting-lifecycle-data-the-smart-way-from-applications-to-it-components/
- Published: 2026-08-13T12:47:44.000Z
- Updated: 2026-08-13T12:47:44.000Z
- Description: Copying lifecycle dates from Applications to IT Components fails the moment relations go m:n. Here's how to merge them into a correct support envelope — and enable it as a switch.
- Author: Thomas Schreiner
- Tags: LeanIX, Enterprise Architecture, Automation, Data Quality, #Import 2026-08-27 21:48

In a healthy LeanIX inventory, lifecycle dates live where people actually maintain them — usually on **Applications**. Business owners know when an app goes live, when it's being phased out, when it hits end-of-life. So far, so good.

But your **IT Components** — the databases, middleware, runtimes, and libraries underneath those applications — often have *empty* lifecycle fields. Nobody maintains them directly, because the truth about "how long do we still need this component?" is really a function of the applications sitting on top of it.

So the obvious idea is: **inherit the lifecycle from Application down to IT Component.** Copy the dates across the relation and you're done.

Except you're not done. Because reality is `m:n`.

## Why 1:1 copying breaks

One IT Component (say, a PostgreSQL cluster) typically serves *many* applications. And one application can depend on *many* components. That's a many-to-many relation.

Now ask the deceptively simple question: **what is the lifecycle of that PostgreSQL cluster?**

- App A goes end-of-life in 2026.
- App B goes end-of-life in 2029.
- App C is still in "plan" and goes active in 2027.

If you naively copy from the "first" or "latest" application, you get a wrong answer. The component's real support obligation is the **union** of all the demands placed on it:

- It has to be **active** as early as the *earliest* application needs it.
- It can only reach **end-of-life** once the *last* application has let go of it.

In other words: you don't pick one source lifecycle. You **merge** them — and each phase follows its own rule (min vs. max).

## The smart merge logic

Here's the core of how our platform computes the resulting lifecycle when many sources feed one target:

```python
def merge_lifecycles(self, lifecycles):
    # union of support obligations across all dependents
    lifecycle = {
        "plan":    min(lc["plan"]    for lc in lifecycles if "plan"    in lc),
        "phaseIn": min(lc["phaseIn"] for lc in lifecycles if "phaseIn" in lc),
        "active":  min(lc["active"]  for lc in lifecycles if "active"  in lc),

        "phaseOut":  max(lc.get("phaseOut", "")  for lc in lifecycles),
        "endOfLife": max(lc.get("endOfLife", "") for lc in lifecycles),
    }
    return lifecycle

```

The intuition:

- **plan** — earliest (min): The component must be planned as soon as *any* app needs planning.
- **phaseIn** — earliest (min): It must be rolling in before the first app goes live.
- **active** — earliest (min): It must be live before the earliest-active app.
- **phaseOut** — latest (max): It can't start winding down until the last app starts winding down.
- **endOfLife** — latest (max): It can only die once the last app is gone.

That single asymmetry — **min for the early phases, max for the late phases** — is the whole trick. It produces the *envelope* that covers every dependent application.

## The subtle bug this avoids

There's one more trap. The five phase dates don't all come from the same source application. `active` might come from App A, while `endOfLife` comes from App B. Merge them blindly and you can produce an **impossible, non-chronological** lifecycle — e.g. a `phaseIn` date that lands *after* the merged `active` date.

So the real implementation sanity-checks the merged result against `active` and discards any phase date that would violate chronological order, rather than emitting a broken lifecycle:

```python
active = lifecycle["active"]
if active:
    if lifecycle["plan"]    > active: lifecycle["plan"] = ""
    if lifecycle["phaseIn"] > active: lifecycle["phaseIn"] = ""
    if lifecycle["phaseOut"] and lifecycle["phaseOut"] < active: lifecycle["phaseOut"] = ""
    if lifecycle["endOfLife"] and lifecycle["endOfLife"] < active: lifecycle["endOfLife"] = ""

```

A merged lifecycle that a human would reject is worse than no lifecycle at all — because it silently poisons every report and roadmap built on top of it.

## When you *don't* want to merge

Sometimes merging is exactly wrong. If a component is shared by ten applications, you might not want its lifecycle driven by all of them — maybe you only trust the inheritance when there's an **unambiguous 1:1 mapping**. That's a legitimate governance choice.

The engine supports this with a custom `filter` expression. This one only inherits from source applications that have a single component relation, and otherwise leaves the component untouched:

```python
return [source_fs[src_field]
        for source_fs in fs.relation(target_to_source_relation)
        if len(source_fs[relation]) <= 1] or None

```

Returning `None` tells the engine "skip this one" — so ambiguous, heavily-shared components are left for a human to decide, instead of being guessed.

## The good news: it's already a switch

Here's the part that matters if you run the **Aronis Automation Platform for SAP LeanIX**: you don't have to write any of this. The lifecycle-merge logic is now a **standard, built-in capability**. You just turn it on with a small configuration:

```json
{
  "name": "Set ITComponent Lifecycle based on Application Lifecycle",
  "fs_types": ["Application", "ITComponent"],
  "relation": "relApplicationToITComponent",
  "fields": [
    {
      "name": "lifecycle",
      "filter": [
        "return [source_fs[src_field] for source_fs in fs.relation(target_to_source_relation) if len(source_fs[relation]) <= 1] or None"
      ]
    }
  ]
}

```

Point it at the relation, name the lifecycle field, optionally add a filter for your governance rules — done. From then on, whenever an application's lifecycle changes, the connected IT Components recompute automatically via webhook. No nightly batch, no manual copy-paste, no stale roadmaps.

## Takeaways

- Lifecycle inheritance across `m:n` relations is **not** a copy — it's a **merge**.
- Use **min for early phases** (plan/phaseIn/active) and **max for late phases** (phaseOut/endOfLife) to get the true support envelope.
- **Validate** the merged result for chronological sanity — a plausible-but-impossible lifecycle is worse than an empty one.
- Decide deliberately when *not* to inherit (e.g. only trust 1:1 mappings) and let humans handle the ambiguous cases.
- In the Aronis Automation Platform, this is a **standard feature you simply enable** — no custom code required.

*Want to see this running on your own workspace?* [*Get in touch.*](https://aronis.de/#contact)