private function connectPorts(
        array $from,
        array $to,
        string $edgeFrom,
        string $edgeTo
    ): NetworkInterface {
        if (!$from[self::COMPONENT]->outPorts()->has($edgeFrom)) {
            throw new InvalidDefinitionException("No outport {$edgeFrom} defined for process {$from[self::NODE_ID]}");
        }

        if (!$to[self::COMPONENT]->inPorts()->has($edgeTo)) {
            throw new InvalidDefinitionException("No inport {$edgeTo} defined for process {$to[self::NODE_ID]}");
        }

        $socket = $this->addHooks(
            new InternalSocket(
                [
                    self::PROCESS => $from,
                    self::PORT => $edgeFrom,
                ],
                [
                    self::PROCESS => $to,
                    self::PORT => $edgeTo,
                ]
            )
        );

        $fromType = $from[self::COMPONENT]->outPorts()->get($edgeFrom)->getAttribute('datatype');
        $toType = $to[self::COMPONENT]->inPorts()->get($edgeTo)->getAttribute('datatype');

        if (!$this->hasValidPortType($fromType)) {
            throw new InvalidDefinitionException(
                "Process {$from[self::NODE_ID]} has invalid outport type {$fromType}. Valid types: " .
                implode(', ', Port::$datatypes)
            );
        }

        if (!$this->hasValidPortType($toType)) {
            throw new InvalidDefinitionException(
                "Process {$to[self::NODE_ID]} has invalid outport type {$toType}. Valid types: " .
                implode(', ', Port::$datatypes)
            );
        }

        // compare out and in ports for datatype definitions
        if (!Port::isCompatible($fromType, $toType)) {
            throw new IncompatibleDatatypeException(
                "Process {$from[self::NODE_ID]}: outport type \"{$fromType}\" of port \"{$edgeFrom}\" ".
                "does not match {$to[self::NODE_ID]} inport type \"{$toType}\" of port \"{$edgeTo}\""
            );
        }

        $from[self::COMPONENT]->outPorts()->get($edgeFrom)->attach($socket);
        $to[self::COMPONENT]->inPorts()->get($edgeTo)->attach($socket);

        return $this;
    }